Phase 7d-A close — Hosted RALPH runtime (Fly.io)
Date: 2026-07-06
Status: Accepted
Plan: docs/plans/2026-07-06-hosted-ralph-runtime.md
Summary
Phase 7d-A stood up a single hosted rocky serve worker on Fly.io and wired rocky-console prod to it. Before: rocky.erid.tech/ralph returned FETCH FAILED · http 500 — console prod had zero RALPH_* env vars, defaulted to RALPH_TRANSPORT=sidecar, tried to spawn a Python child, failed on Vercel serverless. After: /ralph renders zero-state and polls the Fly worker every 5 s.
What shipped
| Slice | Artefact |
|---|---|
| Plan doc | rocky-hq/rocky#100 (docs/plans/2026-07-06-hosted-ralph-runtime.md) |
Container image + fly.toml |
rocky-hq/ralph#6 (Dockerfile, fly.toml, .dockerignore) |
| Env override for bearer | rocky-hq/console#39 + parent bump rocky-hq/rocky#101 (src/lib/ralph/auth.ts prefers RALPH_BEARER_TOKEN env over VAULT) |
| Fly deploy | rocky-ralph-prod.fly.dev — 2× shared-cpu-1x / 512 MB, iad, auto-stop |
| Vercel prod env | RALPH_TRANSPORT=remote, RALPH_URL=https://rocky-ralph-prod.fly.dev, RALPH_BEARER_TOKEN=<64-hex>, RALPH_CRDT_TRANSPORT=disabled |
Verification
curl https://rocky-ralph-prod.fly.dev/healthz→{"ok":true}curl https://rocky-ralph-prod.fly.dev/runs→401(auth gate)curl -H "Authorization: Bearer <token>" .../runs→200rocky.erid.tech/ralph→ RUNS table rendersNO DATA/0 RUNS/LAST FETCH <timestamp>— no 500.
Decisions locked
- Host: Fly.io.
~$5-10/mofor the shared worker; auto-stop trims idle cost. - Deploy config lives in
ralph/.Dockerfile+fly.toml+.dockerignoreare canonical. - CRDT disabled in prod for 7d-A. Prompt edits via
git commitonprompts/*.yaml. Colocation is a 7d-A-4 followup. - Bearer via env override. VAULT is FS-backed and Vercel serverless FS is ephemeral.
RALPH_BEARER_TOKENenv short-circuitsmintBearer; local dev unchanged when env is unset. target_reposemantics deferred. Real ralph submits from prod require clone-on-submit — filed as 7d-A-6 before the shared worker is announced to real operators. Until then,/ralphis a functional zero-state UI, not yet an execution surface.
Known limitations
- Single shared worker: two runs on the same repo will clash on git branches (documented in
ralph/CLAUDE.md). - Bearer is a static HMAC. Per-workspace JWT is Phase 7d-C prereq #2.
- Vercel FS ephemeral: same class of limitation as
polar-customers.jsoncache. Both go away when persistent storage lands. - No autoscaling. Solo-tier probe volume only.
Follow-ups (queued)
| Slice | Blocked by | Description |
|---|---|---|
| 7d-A-4 | — | Optional: colocate CRDT sidecar on Fly if in-console prompt editing is wanted in prod. |
| 7d-A-6 | — | Clone-on-submit for target_repo (GitHub creds via VAULT); precondition before shared worker is announced to real operators. |
| 7d-C-1 | Phase 6b close | Ralph Kustomize base extracted from Path A image. |
| 7d-C-2 | 7d-A-6 | Per-workspace JWT bearer (swap mintBearer static token). |
| 7d-C-3 | 7d-C-1 + 7d-C-2 | Console getRalphClient(workspace_slug) reads endpoint from HEARTH DeploymentRef. |
Related decisions
2026-05-02-rename-fleet-to-hearth— HEARTH is the driver surface Path C will use.2026-05-10-kiln-federation-seam— Phase 8 continues to be blocked-by 6+7, not 7d.2026-07-02-security-incident-tenancy-bypass— Phase 7d-A does not change the middleware invariant; ralph routes remain gated per-request.