Skip to main content
Connect Mibyan to iMessage through 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.
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. The spectrum-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.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

  • A Photon account — sign up at app.photon.codes
  • Node.js: Mibyan uses its managed Node when available. mibyan pm install node provisions the pin; the adapter can fall back to PATH.
  • 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

Either run the unified gateway wizard and pick Photon iMessage:
…or run the Photon setup directly (the wizard calls the same flow):
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 Mibyan 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 ~/.mibyan/photon/sidecar; set PHOTON_SIDECAR_DIR to pin an explicit location.
Runtime credentials are written to ~/.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:
Use mibyan pairing list to see pending codes and approved users. Pre-authorize specific numbers (in ~/.mibyan/.env):
Open access (dev only, in ~/.mibyan/.env):
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 Mibyan responds to every authorized DM and group message. To make group chats opt-in, enable mention gating (DMs still always work):
With 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:
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

You’ll see something like:
Send an iMessage to your assigned number and Mibyan will reply.

Status & troubleshooting

Prints saved credentials, sidecar health, your registered number, and the assigned iMessage line Mibyan uses. When a Photon token and dashboard project are available, status refreshes missing number rows from the dashboard without provisioning new lines.
Common issues:
  • sidecar deps : ✗ run mibyan photon install-sidecar — Node is installed but spectrum-ts isn’t. Run the suggested command.
  • device token : ✗ missing — run mibyan photon setup to log in.
  • No iMessage line assigned yet — Spectrum is enabled but no line has been provisioned; re-run mibyan photon setup or check the dashboard.
  • Sidecar won’t start — confirm node --version is 18.17+ and that mibyan photon install-sidecar completed 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-attachment endpoint. 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-poll endpoint.
  • Read receipts are supported. The sidecar marks an inbound iMessage read after forwarding it to Mibyan, so the sender sees Read without waiting for a model/tool turn. Inbound receipts for Mibyan-sent messages are consumed as presence telemetry and never create an agent turn. Set PHOTON_READ_RECEIPTS=false to keep messages at Delivered.
  • Message effects are supported. Mibyan 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, 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.

Env vars