mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-15 09:21:36 +00:00
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.
52 lines
1.7 KiB
Markdown
52 lines
1.7 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
cd plugins/platforms/photon/sidecar
|
|
npm install
|
|
```
|
|
|
|
The Hermes plugin's `hermes photon setup` command runs `npm install`
|
|
here automatically.
|
|
|
|
## Run standalone
|
|
|
|
For debugging:
|
|
|
|
```bash
|
|
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.
|