hermes-agent/plugins/platforms/photon/sidecar/README.md
Teknium 5b4e431e8c feat(gateway): add Photon Spectrum (iMessage) platform plugin
First-class iMessage support via Photon's managed Spectrum platform.
Targeted as a successor to the BlueBubbles adapter — Photon allocates
the iMessage line, handles delivery, and abuse-prevention so users
don't have to run their own Mac relay. Free tier uses Photon's shared
line pool.

Architecture:
- Inbound: signed JSON webhooks (X-Spectrum-Signature, HMAC-SHA256)
  delivered to a local aiohttp listener. Dedupes on message.id,
  rejects deliveries with >5min timestamp drift.
- Outbound: small supervised Node sidecar that runs the spectrum-ts
  SDK. Photon does not currently expose a public HTTP send-message
  endpoint; the sidecar is the only way to call Space.send() today.
  When Photon ships an HTTP send endpoint we collapse the sidecar
  into _sidecar_send and drop the Node dep — every other layer of
  the plugin stays the same.
- Setup: 'hermes photon login' runs the RFC 8628 device-code flow;
  'hermes photon setup' creates a Spectrum-enabled project, creates
  a shared user (free tier), installs the sidecar's npm deps.
- Webhook management: 'hermes photon webhook register|list|delete'.
- Credentials persisted under credential_pool.photon /
  credential_pool.photon_project in ~/.hermes/auth.json.

Plugin path (not built-in) — per current policy (May 2026), all new
platforms ship under plugins/platforms/. Registers itself via
ctx.register_platform() + ctx.register_cli_command(), zero edits to
core gateway code.

Tests cover:
- HMAC-SHA256 signature verification (happy path, tampered body,
  wrong secret, drift, missing v0 prefix, empty inputs, non-integer
  timestamp)
- Inbound dispatch for text DMs, group ids (any;+;...), and
  attachment metadata markers
- Deduplication window
- check_requirements gating when Node is absent
- Device-code flow: request, header-based token return,
  body-fallback token return, access_denied propagation
- Project/user/webhook API clients with mocked httpx

Known limitations (current Photon API):
- Attachments are metadata only — no download URL yet
- Outbound attachment send not wired (sidecar can add easily)
- Reactions / message effects not exposed yet

Docs: website/docs/user-guide/messaging/photon.md + sidebar entry.
2026-06-08 13:38:30 -07:00

1.7 KiB

Photon sidecar

Small Node helper that bridges Hermes Agent to Photon's Spectrum SDK (spectrum-ts). Hermes is Python; Photon has no public HTTP send-message endpoint today; replies therefore go through this sidecar.

The sidecar:

  • runs Spectrum({ projectId, projectSecret, providers: [imessage.config()] })
  • exposes a loopback-only HTTP control channel for the Python adapter to push send/typing requests (auth via X-Hermes-Sidecar-Token)
  • drains the inbound message stream so spectrum-ts keeps its reconnect/heartbeat machinery alive (real inbound delivery is via Photon's signed webhook hitting our Python aiohttp server)

Install

cd plugins/platforms/photon/sidecar
npm install

The Hermes plugin's hermes photon setup command runs npm install here automatically.

Run standalone

For debugging:

PHOTON_PROJECT_ID=... PHOTON_PROJECT_SECRET=... \
PHOTON_SIDECAR_PORT=8789 PHOTON_SIDECAR_TOKEN=$(openssl rand -hex 16) \
node index.mjs

In normal use, the Python adapter supervises this process — start, restart on crash, kill on shutdown — and never asks the user to run it by hand.

Why a sidecar at all?

Photon publishes webhooks (inbound) but their docs state explicitly:

Pass space.id to Space.send(...) from a separate spectrum-ts SDK instance to reply. No public HTTP send endpoint exists today.

https://photon.codes/docs/webhooks/events

When Photon ships an HTTP send endpoint, the plan is to retire this sidecar entirely and call it directly from Python. The plugin's outbound code path is already isolated behind a single helper (_sidecar_send in adapter.py) to make that swap a one-file change.