Academy → Messaging ChannelsOfficial documentation · Arabic guidance

Photon iMessage

iMessage عبر Photon

Intermediate to advanced7 min readLesson 254 questions✓ 2026-08-18
Before you read

What this page is, and what it holds.

This page covers Photon iMessage. You will use hermes photon setup and hermes send here; about 7 minutes to read. Open the channel to yourself first with an allowlist. An open channel means anyone can message your agent.

8sections
11code examples
1tables
8commands
1,201source words
What you will be able to do

Outcomes taken from this page, not a template.

  • Understand what بوابة المراسلة is and when you need it.
  • Run hermes photon setup and hermes send and understand what happens next.
  • Read the table and take only the row that applies to you.
  • Set PHOTON_SIDECAR_DIR in the right place.
Identifiers you will meet

Exactly as they appear in Hermes.

Commands
  • hermes photon setup
  • hermes send
  • hermes pairing list
  • hermes gateway setup
  • hermes gateway start
  • hermes photon status
  • hermes pairing approve photon
  • hermes photon install-sidecar
Environment variables
  • PHOTON_SIDECAR_DIR
  • PHOTON_PROJECT_ID
  • PHOTON_PROJECT_SECRET
  • PHOTON_ALLOWED_USERS
  • PHOTON_ALLOW_ALL_USERS
  • PHOTON_REQUIRE_MENTION
  • PHOTON_MENTION_PATTERNS
Page map

Jump to the part you need.

  1. 01Architecture
  2. 02Prerequisites
  3. 03First-time setup
  4. 04Authorizing users
  5. 05Start the gateway
  6. 06Status & troubleshooting
  7. 07Limits today
  8. 08Env vars
The full official page

Nothing summarised away.

The documentation body below is reproduced from the official source so commands and identifiers stay exact. Each section carries a short note describing what it contains.

Connect Hermes to iMessage through [Photon][photon], a managed service that handles the Apple line allocation and abuse-prevention layer so you don't have to run your own Mac relay.

The free tier uses Photon's shared iMessage line pool — different recipients may see different sending numbers, but each conversation stays stable. The paid Business tier gives every user the same dedicated number; the plugin supports both, and the free tier is the recommended starting point.

Architecture

Explains the idea itself. Read it slowly; the later sections build on it.

Photon is a persistent-connection channel, like Discord or Slack — no webhook, no public URL, no signing secret to manage.

The spectrum-ts SDK holds a long-lived gRPC stream to Photon for both directions. Because the SDK is TypeScript-only, Hermes runs it in a small supervised Node sidecar and talks to it over loopback:

  • Inbound — the sidecar consumes the SDK's app.messages gRPC stream and forwards each message to the Python adapter over a loopback GET /inbound (NDJSON). The adapter dedupes and dispatches it to the agent, reconnecting automatically if the stream drops.
  • Outbound — replies are loopback POSTs to the sidecar, which calls space.send(...) on the SDK.

The Python plugin starts, supervises, and shuts down the sidecar automatically.

Prerequisites

Explains the idea itself. Read it slowly; the later sections build on it.

  • A Photon account — sign up at [app.photon.codes][app]
  • Node.js 18.17 or newer on PATH (node --version)
  • A phone number that can receive iMessage (used to bind your account)

That's it — there is no public URL or tunnel to set up.

First-time setup

Ordered, practical steps. Run one and confirm it worked before moving on. Commands here: hermes photon setup, hermes gateway setup.

Either run the unified gateway wizard and pick Photon iMessage:

Shell1 line
hermes gateway setup

…or run the Photon setup directly (the wizard calls the same flow):

Shell2 lines
# Device-code login + project + user + sidecar deps, all in one
hermes photon setup --phone +15551234567

The setup, in order:

  1. Device login (client_id=photon-cli) — opens https://app.photon.codes/ for approval and stores the bearer token.
  2. Finds or creates the Hermes Agent project on your account.
  3. Enables Spectrum, reads the project's Spectrum id, and rotates the project secret.
  4. Registers your phone number as a Spectrum user — skipped if a user with that number already exists, so re-running is safe.
  5. Prints your assigned iMessage line — the number you text to reach your agent.
  6. Runs npm install inside the plugin's sidecar directory. On read-only / immutable install trees (hosted Docker images, Podman, Nix) the sidecar automatically falls back to a writable mirror under ~/.hermes/photon/sidecar; set PHOTON_SIDECAR_DIR to pin an explicit location.

Runtime credentials are written to ~/.hermes/.env (PHOTON_PROJECT_ID = the Spectrum project id, PHOTON_PROJECT_SECRET), the same place every other channel keeps its token. Management metadata (device token, dashboard project id) lives in ~/.hermes/auth.json under credential_pool.photon / credential_pool.photon_project.

Authorizing users

Settings you configure once. Change one at a time so you can see what each does. Commands here: hermes pairing list, hermes pairing approve photon. Set PHOTON_ALLOWED_USERS, PHOTON_ALLOW_ALL_USERS in your environment, not in the chat.

Photon uses the same authorization model as every other Hermes channel. Choose one approach:

DM pairing (default). When an unknown number messages your Photon line, Hermes replies with a pairing code. Approve it with:

Shell1 line
hermes pairing approve photon <CODE>

Use hermes pairing list to see pending codes and approved users.

Pre-authorize specific numbers (in ~/.hermes/.env):

Shell1 line
PHOTON_ALLOWED_USERS=+15551234567,+15559876543

Open access (dev only, in ~/.hermes/.env):

Shell1 line
PHOTON_ALLOW_ALL_USERS=true

When PHOTON_ALLOWED_USERS is set, unknown senders are silently ignored rather than offered a pairing code (the allowlist signals you deliberately restricted access).

Require mentions in group chats

By default Hermes responds to every authorized DM and group message. To make group chats opt-in, enable mention gating (DMs still always work):

YAML5 lines
gateway:
  platforms:
    photon:
      enabled: true
      require_mention: true

With require_mention: true, group-chat messages are ignored unless they match a wake-word pattern. The defaults match Hermes and @Hermes agent variants. For a custom agent name, set regex patterns:

YAML6 lines
gateway:
  platforms:
    photon:
      require_mention: true
      mention_patterns:
        - '(?<![\w@])@?amos\b[,:\-]?'

Both keys also accept env vars (PHOTON_REQUIRE_MENTION, PHOTON_MENTION_PATTERNS). This is the same mention-gating model the BlueBubbles iMessage channel uses.

Start the gateway

Commands you type in a terminal. Understand what one does before copying it. Commands here: hermes gateway start.

Shell1 line
hermes gateway start

You'll see something like:

Text1 line
[photon] connected — sidecar on 127.0.0.1:8789, streaming inbound over gRPC

Send an iMessage to your assigned number and Hermes will reply.

Status & troubleshooting

A troubleshooting section. Find the symptom that matches yours rather than reading it end to end. Commands here: hermes photon setup, hermes photon status.

Shell1 line
hermes photon status

Prints saved credentials, sidecar health, your registered number, and the assigned iMessage line Hermes uses. When a Photon token and dashboard project are available, status refreshes missing number rows from the dashboard without provisioning new lines.

Text10 lines
Photon iMessage status
──────────────────────
  device token        : ✓ stored
  dashboard project   : 3c90c3cc-0d44-4b50-...
  spectrum project id : sp-...
  project secret      : ✓ stored
  my number           : +15551234567
  assigned number     : +16282679185
  node binary         : /usr/bin/node
  sidecar deps        : ✓ installed

Common issues:

  • sidecar deps : ✗ run hermes photon install-sidecar — Node is installed but spectrum-ts isn't. Run the suggested command.
  • device token : ✗ missing — run hermes photon setup to log in.
  • No iMessage line assigned yet — Spectrum is enabled but no line has been provisioned; re-run hermes photon setup or check the [dashboard][app].
  • Sidecar won't start — confirm node --version is 18.17+ and that hermes photon install-sidecar completed without errors.

Limits today

Commands you type in a terminal. Understand what one does before copying it. Commands here: hermes send.

  • Inbound attachments are metadata-only. Inbound events carry the filename + MIME type; the agent sees a marker but can't yet read the bytes. The SDK exposes attachment bytes via content.read(), so this is a sidecar follow-up.
  • Outbound attachments are supported. Hermes sends images, voice notes, video, and documents through spectrum-ts' attachment() / voice() content builders via the sidecar's /send-attachment endpoint. Captions arrive as a separate iMessage bubble after the media.
  • Native polls are supported. Hermes sends poll content through spectrum-ts' poll() builder via the sidecar's /send-poll endpoint.
  • Message effects are supported. Hermes sends text with native iMessage bubble/screen effects through spectrum-ts' iMessage effect() builder via the sidecar's /send-effect endpoint.
  • Photon's free quotas: 5,000 messages per server per day, 50 new-conversation initiations per shared line per day. Increases available — email help@photon.codes.
  • Cron and standalone sends need the gateway running. Out-of-process senders (cron jobs, hermes send, the dashboard) reuse the sidecar the gateway spawned — they read its port/token from <hermes-home>/runtime/photon-sidecar.json, written once the sidecar passes its health check and removed when it stops. If a standalone send reports the gateway appears to be down, start (or restart) the gateway first.
  • Shared/free-tier lines can't initiate conversations with new targets. Photon-side policy: a shared line can only message a number after that number has texted the line first. A cron/standalone send to a brand-new recipient will be rejected by Photon even when Hermes is set up correctly — either have the recipient message the line once, or move to a dedicated line.

Env vars

A lookup table. Do not read it all; find the row that applies to you.

VariableDefaultNotes
PHOTON_PROJECT_IDfrom .envSpectrum project id (the SDK's projectId); set by setup
PHOTON_PROJECT_SECRETfrom .envProject secret; set by setup
PHOTON_SIDECAR_PORT8789Loopback port for the sidecar control + inbound channel
PHOTON_SIDECAR_AUTOSTARTtrueWhether the adapter spawns the sidecar
PHOTON_NODE_BINwhich nodeOverride the Node binary path
PHOTON_HOME_CHANNEL(unset)Default space id for cron / notifications
PHOTON_HOME_CHANNEL_NAME(unset)Human label for the home channel
PHOTON_ALLOWED_USERS(unset)Comma-separated E.164 allowlist
PHOTON_ALLOW_ALL_USERSfalseDev only — accept any sender
PHOTON_REQUIRE_MENTIONfalseRequire a wake word before responding in groups
PHOTON_MENTION_PATTERNSHermes wake wordsJSON list / comma / newline regex patterns for group mentions
PHOTON_DASHBOARD_HOSTapp.photon.codesOverride the dashboard / device-login host
PHOTON_SPECTRUM_HOSTspectrum.photon.codesOverride the Spectrum API host

[photon]: https://photon.codes/ [app]: https://app.photon.codes/

Knowledge check

4 questions answered by this page alone.

Every option is a real identifier from the Hermes documentation. The wrong ones are real too, just from other pages.

1. In this lesson's table, what is the “Default” for “PHOTONDASHBOARDHOST”?
2. Which of these environment variables actually appears in this lesson?
3. Which of these headings does not appear in this lesson?
4. Which configuration key appears in this lesson's examples?