{"protocol":"clank-doc/1","frameworkVersion":"0.8.0","slug":"railway","title":"Railway production deployment","description":"This profile runs the Clank control plane and its trusted application processes in one Railway service. Railway terminates TLS and forwards every control plane and application hostname to the same listener; Clank then routes application hos","group":{"id":"deploy","title":"Deploy and operate"},"url":"https://docs.clank.run/docs/railway","source":"docs/railway.md","headings":["Topology","Repository configuration","Domains","First account and CLI","Operations"],"tableOfContents":[{"id":"topology","title":"Topology","level":2},{"id":"repository-configuration","title":"Repository configuration","level":2},{"id":"domains","title":"Domains","level":2},{"id":"first-account-and-cli","title":"First account and CLI","level":2},{"id":"operations","title":"Operations","level":2}],"markdown":"# Railway production deployment\n\nThis profile runs the Clank control plane and its trusted application processes in one Railway\nservice. Railway terminates TLS and forwards every control-plane and application hostname to the\nsame listener; Clank then routes application hosts to their supervised loopback processes.\n\n## Topology\n\n```text\nclank.run ───────────────┐\n*.apps.clank.run ────────┼─ Railway edge ─ Clank control plane :$PORT\napproved custom domains ┘                    ├─ app :4300 ─ projects/<id>/data/app.sqlite\n                                             ├─ app :4301 ─ projects/<id>/data/app.sqlite\n                                             └─ /data/control.sqlite\n\nRailway volume mounted at /data\n```\n\nUse exactly one replica. Clank's control database and each application database use SQLite, and the\nincluded application supervisor is single-leader. Horizontal replicas need the external storage,\nleader, and remote-runner integrations described in [Self-hosting](self-hosting.md).\n\n## Repository configuration\n\n- `Dockerfile` builds the dependency-free TypeScript sources and copies only the production\n  control-plane runtime into the final image.\n- `railway.json` selects the Dockerfile, probes storage-backed `/_clank/readyz`, gives graceful\n  shutdown 30 seconds, and restarts failed processes. The reserved path is checked before\n  application-host dispatch because Railway sends its own health-check hostname.\n- A Railway volume must be mounted at `/data`.\n\nThe service needs these variables:\n\n```sh\nHOST=0.0.0.0\nNODE_ENV=production\nTRUST_PROXY=1\nCLANK_PLATFORM_URL=https://clank.run\nCLANK_PLATFORM_DATA=/data\nCLANK_PLATFORM_MASTER_KEY=<base64url-encoded 32-byte secret>\nCLANK_PLATFORM_ADMIN_EMAILS=operator@example.com\nCLANK_SIGNUP=bootstrap\nCLANK_RUNNER=process\nCLANK_INGRESS=1\nCLANK_INGRESS_BASE_DOMAIN=apps.clank.run\nCLANK_APP_URL_TEMPLATE=https://{slug}.apps.clank.run\nCLANK_CUSTOM_DOMAIN_TARGET=apps.clank.run\nCLANK_MAX_PROJECTS_PER_ACCOUNT=10\nCLANK_MAX_PROJECTS_PER_ORGANIZATION=10\nCLANK_MAX_DOMAINS_PER_PROJECT=5\nCLANK_METRICS_RETENTION_DAYS=30\nCLANK_BACKUP_INTERVAL_MS=86400000\nCLANK_BACKUP_MAX_COUNT=30\nCLANK_BACKUP_MAX_AGE_MS=7776000000\nCLANK_ALLOW_UNSAFE_MIGRATIONS=0\n```\n\nDo not put `CLANK_PLATFORM_MASTER_KEY` in source control. Back it up separately from the volume:\nlosing it makes encrypted secrets and recovery points unreadable.\n\n`bootstrap` lets the first browser user create the only public account, then closes registration.\nAdditional people join through email-bound invitations created by an owner or administrator.\n`CLANK_PLATFORM_ADMIN_EMAILS` is an exact, comma-separated operator allowlist. Matching accounts\nreceive the separate `platform_admin` role; removing an address revokes that role on the next\ncontrol-plane start. Global administration is available only to an interactive browser session,\nnever to a CLI bearer token. After signing in, an allowlisted operator can open `/admin` for\ninstallation-wide analytics and the redacted account directory. Read-only support impersonation is\nshort-lived, recent-auth gated, reason-bound, visibly labeled, and audited; it is not a substitute\nfor limiting and protecting the operator allowlist.\n\n## Domains\n\nAttach both `clank.run` and `*.apps.clank.run` to the Railway service. Publish every routing, ACME,\nand TXT validation record Railway returns.\n\n`clank.run` is registered with and delegated to Vercel DNS. Replace Vercel's default apex and\nwildcard routing aliases while preserving its CAA records:\n\n- add an apex `ALIAS` targeting the hostname Railway supplies for `clank.run`;\n- add the `*.apps` and `_acme-challenge.apps` CNAME records Railway supplies;\n- add the `_railway-verify` and `_railway-verify.apps` TXT records Railway supplies.\n\nThe wildcard gives every project an immediate `https://<slug>.apps.clank.run` URL. Railway must also\nknow about a customer-owned custom domain before its edge can issue a certificate for that host.\nFor the current single-tenant deployment, add the hostname to the same service with:\n\n```sh\nrailway domain customer.example --service clank\n```\n\nThen publish Railway's validation records plus the ownership record shown in the Clank console.\nClank will route the host only after both ownership and routing checks succeed.\n\n## First account and CLI\n\n1. Open `https://clank.run` and create the bootstrap owner with a unique password of at least 8\n   characters.\n2. In a local checkout, start browser-assisted device authorization:\n\n   ```sh\n   clank login\n   ```\n\n3. Create or scaffold an application and deploy it:\n\n   ```sh\n   clank create my-app\n   cd my-app\n   clank deploy\n   ```\n\n`clank create` scaffolds the authenticated to-do starter by default, including its migrations and\ndeployment manifest.\n\nEach project receives a separate directory and SQLite database under `/data/projects/<project-id>`.\nDatabase migrations are planned and applied by Clank during deployment; a failed health check or\nmigration leaves the active release unchanged.\n\nManaged application processes receive reserved runtime values, including\n`TRUST_PROXY=1`, an empty `ALLOWED_HOSTS`, and `CLANK_MANAGED_INGRESS=1`.\nTogether they let the Node adapter reconstruct each canonical or verified\ncustom-domain origin while the loopback-only ingress remains responsible for\nexact host admission. Application manifests cannot override these values.\n\n## Operations\n\nBefore each framework upgrade, download or snapshot the Railway volume and preserve the external\nmaster key. After deployment, verify:\n\n```sh\ncurl --fail https://clank.run/livez\ncurl --fail https://clank.run/readyz\ncurl --fail https://clank.run/_clank/readyz\nrailway logs --service clank --lines 100\n```\n\nAlso verify browser session refresh, CLI device authorization, a disposable deployment, its\nwildcard application URL, backup creation, rollback, and permanent deletion.\n\nThe Railway deployment uses Clank's `process` runner because hosted Railway containers do not expose\na Docker daemon. This runner is intentionally for trusted deployers: application code runs with the\nsame container and volume authority as the control plane. Use Clank's Docker runner or a remote\nsandbox worker before opening deployment access to mutually untrusted users.\n"}