Free to startPhoton’s shared-line pool is free. No subscription is required to send
your first iMessage from Mibyan — just a phone number we can bind to
your account.
Architecture
Photon is a persistent-connection channel, like Discord or Slack — no webhook, no public URL, no signing secret to manage. Thespectrum-ts SDK holds a long-lived gRPC stream to Photon for
both directions. Because the SDK is TypeScript-only, Mibyan runs it in a
small supervised Node sidecar and talks to it over loopback:
- Inbound — the sidecar consumes the SDK’s
app.messagesgRPC stream and forwards each message to the Python adapter over a loopbackGET /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.
Prerequisites
- A Photon account — sign up at app.photon.codes
- Node.js: Mibyan uses its managed Node when available.
mibyan pm install nodeprovisions the pin; the adapter can fall back to PATH. - A phone number that can receive iMessage (used to bind your account)
First-time setup
Either run the unified gateway wizard and pick Photon iMessage:- Device login (
client_id=photon-cli) — openshttps://app.photon.codes/for approval and stores the bearer token. - Finds or creates the
Mibyanproject on your account. - Enables Spectrum, reads the project’s Spectrum id, and rotates the project secret.
- Registers your phone number as a Spectrum user — skipped if a user with that number already exists, so re-running is safe.
- Prints your assigned iMessage line — the number you text to reach your agent.
- Runs
npm installinside 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~/.mibyan/photon/sidecar; setPHOTON_SIDECAR_DIRto pin an explicit location.
~/.mibyan/.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 ~/.mibyan/auth.json under
credential_pool.photon / credential_pool.photon_project.
Authorizing users
Photon uses the same authorization model as every other Mibyan channel. Choose one approach: DM pairing (default). When an unknown number messages your Photon line, Mibyan replies with a pairing code. Approve it with:mibyan pairing list to see pending codes and approved users.
Pre-authorize specific numbers (in ~/.mibyan/.env):
~/.mibyan/.env):
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 Mibyan responds to every authorized DM and group message. To make group chats opt-in, enable mention gating (DMs still always work):require_mention: true, group-chat messages are ignored unless
they match a wake-word pattern. The defaults match Mibyan and
@Mibyan agent variants. For a custom agent name, set regex patterns:
PHOTON_REQUIRE_MENTION,
PHOTON_MENTION_PATTERNS). This is the same mention-gating model the
BlueBubbles iMessage channel uses.
Start the gateway
Status & troubleshooting
status refreshes missing number rows from the dashboard
without provisioning new lines.
sidecar deps : ✗ run mibyan photon install-sidecar— Node is installed butspectrum-tsisn’t. Run the suggested command.device token : ✗ missing— runmibyan photon setupto log in.No iMessage line assigned yet— Spectrum is enabled but no line has been provisioned; re-runmibyan photon setupor check the dashboard.- Sidecar won’t start — confirm
node --versionis 18.17+ and thatmibyan photon install-sidecarcompleted without errors.
Limits today
- 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. Mibyan sends images, voice
notes, video, and documents through spectrum-ts’
attachment()/voice()content builders via the sidecar’s/send-attachmentendpoint. Captions arrive as a separate iMessage bubble after the media. - Native polls are supported. Mibyan sends poll content through
spectrum-ts’
poll()builder via the sidecar’s/send-pollendpoint. - Read receipts are supported. The sidecar marks an inbound iMessage
read after forwarding it to Mibyan, so the sender sees
Readwithout waiting for a model/tool turn. Inbound receipts for Mibyan-sent messages are consumed as presence telemetry and never create an agent turn. SetPHOTON_READ_RECEIPTS=falseto keep messages atDelivered. - Message effects are supported. Mibyan sends text with native iMessage
bubble/screen effects through spectrum-ts’ iMessage
effect()builder via the sidecar’s/send-effectendpoint. - 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,
mibyan send, the dashboard) reuse the sidecar the gateway spawned — they read its port/token from<mibyan-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 Mibyan is set up correctly — either have the recipient message the line once, or move to a dedicated line.

