mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-22 10:32:00 +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.
117 lines
5.3 KiB
Markdown
117 lines
5.3 KiB
Markdown
# Photon iMessage platform plugin
|
|
|
|
This plugin connects Hermes Agent to iMessage (and WhatsApp Business +
|
|
future Spectrum interfaces) through [Photon][photon] — a managed
|
|
service that handles the iMessage line allocation, delivery, and
|
|
abuse-prevention layer so users don't have to run their own Mac
|
|
relay.
|
|
|
|
The free tier uses Photon's shared iMessage line pool (`type: shared`)
|
|
and is the path we recommend for everyone who doesn't already pay for a
|
|
dedicated number.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌─────────────────────────┐ HMAC-signed POSTs ┌──────────────────┐
|
|
│ Photon Spectrum cloud │ ──────────────────────► │ Hermes Agent │
|
|
│ (iMessage line owner) │ │ (Python) │
|
|
└─────────────────────────┘ JSON over loopback │ │
|
|
▲ ◄────────────────────── │ PhotonAdapter │
|
|
│ │ + aiohttp recv │
|
|
│ spectrum-ts │ │
|
|
│ SDK (Node) │ spawns + super- │
|
|
▼ │ vises ▼ │
|
|
┌─────────────────────────┐ ├──────────────────┤
|
|
│ Node sidecar │ ◄──── X-Hermes- ─ │ Node sidecar │
|
|
│ (plugins/.../sidecar) │ Sidecar-Token │ child process │
|
|
└─────────────────────────┘ └──────────────────┘
|
|
```
|
|
|
|
Inbound traffic is webhook-only — Hermes runs an aiohttp listener
|
|
that verifies `X-Spectrum-Signature` and dedupes on `message.id`.
|
|
|
|
Outbound traffic goes through a tiny Node sidecar that runs the
|
|
`spectrum-ts` SDK. Photon does not currently expose an HTTP
|
|
send-message endpoint; their own docs say:
|
|
|
|
> 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, `_sidecar_send` is the one
|
|
function that swaps and the sidecar disappears. The rest of the
|
|
plugin stays the same.
|
|
|
|
## First-time setup
|
|
|
|
```bash
|
|
# 1. Log in via the device-code flow (opens browser)
|
|
hermes photon login
|
|
|
|
# 2. Full setup: project, user, sidecar deps
|
|
hermes photon setup --phone +15551234567
|
|
|
|
# 3. Expose your webhook URL to the public internet
|
|
# (cloudflared, ngrok, your gateway's public hostname, etc.)
|
|
# Then register it with Photon:
|
|
hermes photon webhook register https://your-host.example.com/photon/webhook
|
|
|
|
# 4. Save the signing secret it prints to ~/.hermes/.env
|
|
# as PHOTON_WEBHOOK_SECRET=...
|
|
# Photon only returns it ONCE.
|
|
|
|
# 5. Start the gateway
|
|
hermes gateway start --platform photon
|
|
```
|
|
|
|
## Credentials
|
|
|
|
Stored in `~/.hermes/auth.json` under `credential_pool`:
|
|
|
|
```jsonc
|
|
{
|
|
"credential_pool": {
|
|
"photon": [
|
|
{ "access_token": "<dashboard-bearer>", "issued_at": ... }
|
|
],
|
|
"photon_project": [
|
|
{ "project_id": "...", "project_secret": "...", "name": "Hermes Agent" }
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
The per-URL webhook signing secret is treated like an API key and
|
|
lives in `~/.hermes/.env` as `PHOTON_WEBHOOK_SECRET`.
|
|
|
|
## Configuration knobs
|
|
|
|
All env vars are documented in `plugin.yaml`. The most important are:
|
|
|
|
| Env var | Default | Meaning |
|
|
|--------------------------|--------------------|-----------------------------------------|
|
|
| `PHOTON_PROJECT_ID` | from auth.json | Spectrum project ID |
|
|
| `PHOTON_PROJECT_SECRET` | from auth.json | Spectrum project secret (HTTP Basic) |
|
|
| `PHOTON_WEBHOOK_SECRET` | (unset) | Signing secret returned at register |
|
|
| `PHOTON_WEBHOOK_PORT` | 8788 | Local port for the aiohttp listener |
|
|
| `PHOTON_WEBHOOK_PATH` | /photon/webhook | Path under which the listener mounts |
|
|
| `PHOTON_SIDECAR_PORT` | 8789 | Loopback port for sidecar control |
|
|
| `PHOTON_HOME_CHANNEL` | (unset) | Default space ID for cron delivery |
|
|
| `PHOTON_ALLOWED_USERS` | (unset) | Comma-separated E.164 allowlist |
|
|
|
|
## Limitations (current Photon API)
|
|
|
|
- **Attachments are metadata only.** Inbound webhooks include the
|
|
filename + MIME type but no download URL. The plugin surfaces a
|
|
text marker (`[Photon attachment received: …]`) so the agent knows
|
|
something arrived, but cannot read the bytes. Photon's docs note
|
|
an attachment retrieval endpoint is on the roadmap.
|
|
- **Outbound attachments are not supported yet.** Adding them is
|
|
straightforward once the sidecar wires up `attachment(...)` /
|
|
`space.send(attachment(...))` from `spectrum-ts`.
|
|
- **Reactions, message effects, polls** — not exposed yet; the
|
|
`spectrum-ts` SDK supports them, and the sidecar is the natural
|
|
place to add them when the agent has reason to use them.
|
|
|
|
[photon]: https://photon.codes/
|