···6677- [`reference/api.md`](reference/api.md) — Go search API service
88- [`reference/app.md`](reference/app.md) — Ionic Vue mobile app
99+- [`reference/deployment-walkthrough.md`](reference/deployment-walkthrough.md) — Railway deployment guide
910- [`reference/lexicons.md`](reference/lexicons.md) — Tangled AT Protocol record types
1011- [`reference/resync.md`](reference/resync.md) — Backfill and repo-resync recovery playbook
1112
+150
docs/reference/deployment-walkthrough.md
···11+# Deployment Walkthrough
22+33+This repo maps cleanly to Railway, but only for the backend pieces.
44+55+- Deploy `packages/api` to Railway as two services: `api` and `indexer`.
66+- Keep the Ionic + Capacitor app on your machine or in CI for native builds.
77+- Point the mobile app at the Railway `api` service with
88+ `VITE_TWISTER_API_BASE_URL`.
99+1010+## What Railway Should Host
1111+1212+Railway is a good home for the Go services in this repo:
1313+1414+- `api`: serves HTTP routes, docs, search, proxies, and readiness checks
1515+- `indexer`: consumes Tap, writes into Turso, and exposes its own health endpoint
1616+Railway is not the place that ships the native iOS or Android app. You still
1717+build, sign, and distribute the Capacitor shells separately.
1818+1919+## Prerequisites
2020+2121+Before you start, have these ready:
2222+2323+- a Railway account and the Railway CLI
2424+- a Turso database URL and auth token
2525+- a Tap URL and Tap auth password
2626+- a seed list for the first backfill run
2727+From this machine:
2828+2929+```sh
3030+cd /Users/owais/Projects/Twisted
3131+railway login
3232+```
3333+3434+## Create The Railway Project
3535+3636+In the Railway dashboard, create one empty project with two empty services:
3737+3838+- `api`
3939+- `indexer`
4040+Then link this repo to that project:
4141+4242+```sh
4343+cd /Users/owais/Projects/Twisted
4444+railway link
4545+```
4646+4747+## Configure Service Shape
4848+4949+Both services should deploy from the same local path:
5050+5151+- path: `packages/api`
5252+- build source: `packages/api/Dockerfile`
5353+Set the service start commands in Railway:
5454+- `api`: `twister api`
5555+- `indexer`: `twister indexer`
5656+The checked-in Dockerfile already builds the `twister` binary.
5757+5858+## Set Variables
5959+6060+Use shared variables for values both services need:
6161+6262+- `TURSO_DATABASE_URL`
6363+- `TURSO_AUTH_TOKEN`
6464+- `LOG_LEVEL=info`
6565+- `LOG_FORMAT=json`
6666+Set these on `api`:
6767+- `HTTP_BIND_ADDR=0.0.0.0:${{ PORT }}`
6868+- `SEARCH_DEFAULT_LIMIT=20`
6969+- `SEARCH_MAX_LIMIT=100`
7070+- `READ_THROUGH_MODE=missing`
7171+- `READ_THROUGH_COLLECTIONS=sh.tangled.*`
7272+- `READ_THROUGH_MAX_ATTEMPTS=5`
7373+- `ENABLE_ADMIN_ENDPOINTS=false`
7474+- `ADMIN_AUTH_TOKEN=<set this if admin routes are enabled>`
7575+Set these on `indexer`:
7676+- `INDEXER_HEALTH_ADDR=0.0.0.0:${{ PORT }}`
7777+- `TAP_URL=<your Tap URL>`
7878+- `TAP_AUTH_PASSWORD=<your Tap password>`
7979+- `INDEXED_COLLECTIONS=sh.tangled.*`
8080+- `ENABLE_INGEST_ENRICHMENT=true`
8181+Optional OAuth variables for a Railway-hosted web client metadata endpoint:
8282+- `OAUTH_CLIENT_ID`
8383+- `OAUTH_REDIRECT_URIS`
8484+The `${{ PORT }}` reference matters. Railway health checks run against the
8585+service port it injects, so the process must listen on that port.
8686+8787+## Deploy From This Machine
8888+8989+From the repo root, deploy `packages/api` into each Railway service:
9090+9191+```sh
9292+cd /Users/owais/Projects/Twisted
9393+railway up packages/api --path-as-root --service api
9494+railway up packages/api --path-as-root --service indexer
9595+```
9696+9797+`--path-as-root` is important in this monorepo. It makes `packages/api` the
9898+deployment root instead of archiving the whole repo.
9999+100100+## Configure Health Checks
101101+102102+Set the health check path in Railway for each service:
103103+104104+- `api`: `/readyz`
105105+- `indexer`: `/health`
106106+`/readyz` is the better API check because it verifies database reachability.
107107+108108+## First Bootstrap
109109+110110+A fresh environment is not search-ready just because the services booted.
111111+112112+1. Deploy `api`.
113113+2. Deploy `indexer`.
114114+3. Confirm the `api` domain returns `200` from `/readyz`.
115115+4. Confirm the `indexer` returns `200` from `/health`.
116116+5. Run the initial backfill against the same Turso and Tap environment.
117117+One simple way to run backfill from this machine is to use the same env values
118118+locally and execute:
119119+120120+```sh
121121+cd /Users/owais/Projects/Twisted/packages/api
122122+go run ./main.go backfill --seeds /path/to/seeds.txt
123123+```
124124+125125+Do not call the environment ready until that first backfill has completed.
126126+127127+## Point The App At Railway
128128+129129+For local app builds, set the Railway API URL in `apps/twisted/.env`:
130130+131131+```sh
132132+VITE_TWISTER_API_BASE_URL=https://<your-api-domain>
133133+```
134134+135135+Then build or run the app as usual:
136136+137137+```sh
138138+pnpm --dir apps/twisted dev
139139+pnpm --dir apps/twisted build
140140+pnpm --dir apps/twisted exec cap sync
141141+```
142142+143143+## Operating Model
144144+145145+This is the practical split:
146146+147147+- Railway hosts the always-on backend
148148+- Turso stores indexed data
149149+- this machine, or CI, builds the mobile app and points it at Railway
150150+If you later want a Railway-hosted web frontend, add that as a separate service.