Deploy a private iMessage agent powered by Photon Spectrum, Codex, PostgreSQL, and optional Supermemory.
The canonical repository is tecxbro/imessge-agent-railway. The Railway service URL opens the public setup dashboard; conversations happen through iMessage.
You need:
- a Railway account;
- a Photon account for Spectrum Cloud iMessage setup;
- the owner's personal phone number allowed to message the agent;
- either a ChatGPT account with Codex device login enabled or an OpenAI API key; and
- an optional Supermemory API key if you want semantic memory.
The production shape uses one Railway application service, one PostgreSQL 18 service, and one persistent volume. Keep the application at one replica because its Codex credentials and workspaces live on the attached volume. Review current Railway pricing before provisioning resources.
- Create a Railway project and add a PostgreSQL 18 service.
- Add an application service from this repository, select branch
main, and configure/railway.jsonas its Railway config file. - Attach one volume to the application service at
/var/dataand keep the service at one replica. - Set
DATABASE_URL=${{Postgres.DATABASE_URL}},NODE_ENV=production,CODEX_HOME=/var/data/codex, andAGENT_WORKSPACE_ROOT=/var/data/workspaces. - Generate
APP_ENCRYPTION_KEYwithopenssl rand -base64 32and store it as a Railway service variable. Existing installations must preserve both this key and their explicitDEPLOYMENT_ID; a new installation may derive its stable internal ID from Railway'sRAILWAY_SERVICE_ID. - Choose
CODEX_AUTH_MODE=chatgptorCODEX_AUTH_MODE=api_key. For API-key mode, addOPENAI_API_KEYas a private Railway service variable. - Enable Wait for CI before enabling GitHub automatic deployments from
main, deploy, and wait for the build, pre-deploy migration, start, and/healthzcheck to finish. - Open the public dashboard at the application URL, enter the owner's phone number, authenticate Photon, then connect ChatGPT when using ChatGPT mode.
- After ChatGPT connects, open Advanced and confirm or change the deployment model and reasoning effort.
- Confirm
/readyzreturns HTTP 200. - Text the Photon-assigned number shown at completion from the configured owner phone.
Railway provides PORT, RAILWAY_SERVICE_ID, RAILWAY_DEPLOYMENT_ID, and RAILWAY_VOLUME_MOUNT_PATH at runtime. Do not define fake local values for those platform inputs.
Dashboard setup requests require a same-origin browser request and reject cross-site fetch metadata.
The dashboard collects the owner's phone number before Photon setup. U.S. entry is the default, so the owner can type a normal 10-digit number without +1; Not in the U.S.? reveals a country selector, and international users may enter a national or complete international number. The server validates the selected country and normalizes the value to E.164 before storage. The phone number is configured in the dashboard rather than required in the environment. It is encrypted and fingerprinted in PostgreSQL, becomes the only iMessage sender authorized to use the agent, and is registered during Photon owner provisioning. The different Photon-assigned number shown at completion is the destination the owner texts. Replacing the owner number in the dashboard revokes the previous owner identity before the new identity can authorize messages.
The default CODEX_AUTH_MODE is chatgpt.
- Open the deployed application URL in a trusted browser.
- Save the owner phone and finish Photon setup first, then select Connect ChatGPT.
- Open the displayed device-auth URL, sign in, and enter the one-time code.
- Keep the dashboard open while it verifies the login and prepares Codex. The dashboard advances automatically when each stage is ready.
After ChatGPT connects, Advanced displays the account plan and only the models and reasoning efforts advertised by Codex for that account. The stored deployment default is GPT-5.6 Luna with High reasoning. If that exact pair is unavailable, the agent uses Codex's advertised default pair without changing the stored preference, and Advanced explains the fallback. Saved changes apply to new message chains; running work keeps its chain snapshot.
ChatGPT credentials persist under /var/data/codex. Treat auth.json like a password: never print it, copy it into a ticket, or commit it.
If ChatGPT authentication needs operator recovery, open a private railway ssh session and run npm run codex:login, then npm run codex:status before restarting the service.
Set these application service variables and redeploy:
CODEX_AUTH_MODE=api_key
OPENAI_API_KEY=replace-with-a-Railway-secretAPI-key mode does not require device login. The runtime supplies the key only to the Codex child process through an explicit environment allowlist.
Open the Railway-generated application URL or check the endpoints directly:
curl --fail --silent "https://<service-host>/healthz"
curl --silent --show-error "https://<service-host>/readyz"/healthzreturning 200 means the HTTP process is alive./readyzreturning 200 means owner identity, critical storage, PostgreSQL, migration, queue, Codex, and Spectrum checks are ready./readyzreturning 503 means setup is incomplete or a dependency is degraded. Its response includes the detailed component snapshot and remediation actions.
The root URL opens the setup dashboard; it is not an iMessage chat. The dashboard reports provider setup status, device codes, verification URLs, the assigned number, masked owner information, detailed readiness, and bounded provider error codes. Raw owner phone values, provider access tokens, project secrets, Codex credentials, database credentials, and unrestricted provider errors remain server-side.
After /readyz is 200:
- Send a direct iMessage to the Spectrum-connected line from the configured owner phone number.
- Confirm the agent sends one terminal response.
- Send from an unauthorized handle and confirm no Codex process starts.
- Restart the Railway application service, wait for
/readyzto return 200, and send a follow-up.
Record protected live evidence before describing any provider path as live-working. Offline tests and a healthy /healthz are not substitutes for an authorized live message.
The checked-in railway.json configures the application service with:
- Railpack and the supported Node runtime range;
npm ci --include=dev && npm run build;npm run db:migratebefore each deploy;npm startas the production command;/healthzas Railway's liveness check;- zero deployment overlap so two releases cannot consume the same Spectrum stream; and
- a 90-second draining window for graceful shutdown.
Create the PostgreSQL service and /var/data volume in the Railway project. Use /var/data/codex for Codex credentials and sessions, /var/data/workspaces for agent workspaces, and DATABASE_URL=${{Postgres.DATABASE_URL}} for the database connection.
The volume-backed design makes this version single-instance. Do not enable horizontal scaling without redesigning credential and workspace ownership.
The most common edits are:
| Goal | File or setting |
|---|---|
| Personality and conversational style | prompts/interaction.system.md, prompts/voice-policy.md |
| Execution behavior | prompts/execution.system.md |
| Approval rules | prompts/approval-policy.md |
| Models and reasoning effort | Advanced in the deployment dashboard |
| Authorized sender | Change phone number in the deployment dashboard; owner environment values are migration inputs only |
| Semantic memory | SUPERMEMORY_API_KEY |
| Railway build and deploy behavior | railway.json |
| Service, database, volume, variables, and networking | Railway project settings |
| Concurrency and runtime limits | .env |
| Tools or repository workspaces | src/runtime/production-bootstrap.ts capability composition |
See Customization before editing the runtime composition or approval policy.
- Node.js 22.12 through 22.x, or Node.js 24 or newer;
- PostgreSQL 13 or newer;
- a Photon account for dashboard setup, or existing Spectrum credentials;
- ChatGPT device authentication or an OpenAI API key; and
- a Supermemory key only if memory is enabled.
git clone https://github.com/tecxbro/imessge-agent-railway.git
cd imessge-agent-railway
npm ci
npm run repo:setup-guards
cp .env.example .envEdit .env, then generate an APP_ENCRYPTION_KEY:
openssl rand -base64 32Use separate, absolute paths for CODEX_HOME and AGENT_WORKSPACE_ROOT; .env does not expand $HOME or $PWD. Apply migrations, authenticate Codex if using the CLI flow, and start the composed service:
npm run db:migrate
npm run codex:login
npm run codex:status
npm run devBefore proposing a change, run:
npm run repo:verify-target
npm run typecheck
npm test
npm run test:security
npm run test:integration
npm run test:chaos
npm run build
npm run docs:check
npm run railway:validateDatabase-backed integration coverage needs a separate disposable database in POSTGRES_PIPELINE_TEST_DATABASE_URL; the suite truncates application tables. Protected provider tests are opt-in and require private accounts.
Authorized iMessage owner
↕
Photon Spectrum Cloud (persistent app.messages gRPC stream)
↓
Authorize, normalize, persist, and schedule
↓
PostgreSQL + pg-boss durable pipeline
↓
Interaction Codex thread
├─ direct response
└─ bounded execution threads
↓
Materialized outbound parts + restart-safe cursor
↓
Photon Spectrum Cloud
Successful turn ──> optional curated Supermemory projection
src/server.ts is the executable entrypoint. It loads the production adapters from src/runtime/production-bootstrap.ts and passes them to the generic lifecycle in src/index.ts. HTTP liveness starts first; PostgreSQL, migrations, queue workers, Codex checks, optional memory, reconciliation, and Spectrum follow in dependency order.
PostgreSQL is the operational source of truth. Supermemory stores only bounded, curated semantic facts and summaries. The receive loop never runs model work inline.
- Unknown senders are rejected before persistence, queueing, or model work.
- Dashboard setup mutations require same-origin and fetch-metadata validation.
- Authorization is checked again immediately before Codex or another child process starts.
- Codex children receive an explicit environment allowlist, never the full server environment.
- Models cannot approve actions or broaden code-owned permission profiles.
- Queue payloads contain identifiers, not raw personal content.
- Logs, health endpoints, and failure records must remain redacted.
- Codex credentials and workspaces use separate private directories on the persistent volume.
Never commit .env, auth.json, provider credentials, database URLs, owner handles, or workspace data. Read Security and privacy for the full trust model.
The executable production runtime is composed. Clean-account Railway deployment and protected live-provider evidence remain separate release checks.
The repository does not currently include recorded evidence for a fresh Railway deployment, dashboard Photon enrollment, dashboard ChatGPT enrollment, live Photon/Spectrum authorized DM, authenticated Codex restart/resume, or live Supermemory add/search/delete cycle. Run the release smoke checklist for the exact commit under review.
The pinned Spectrum API sends through native space.send(...) but does not accept the database's stable client GUID. PostgreSQL prevents normal resend, but a crash after provider acknowledgement and before cursor checkpoint can duplicate one bubble. Do not claim exactly-once provider delivery without protected live evidence and provider support.
A blank Railway volume has no code-owned execution workspace capability. The default deployment can answer conversational turns; repository execution requires an explicit reviewed workspace/capability binding.