2026-05-09 — petrova.host v0 launch (Fleet MCP server live on Railway)
rank: decision outranks: [finding, runbook]
Section titled “rank: decision outranks: [finding, runbook]”2026-05-09 — petrova.host v0 launch (Fleet MCP server live on Railway)
Section titled “2026-05-09 — petrova.host v0 launch (Fleet MCP server live on Railway)”Date: 2026-05-09 Status: closed Supersedes: none Superseded-by: none — current
Context
Section titled “Context”PRs #36 (spec), #37 (plan) and #38 (implementation, Tasks 1–16) shipped the v0 read-only Fleet MCP server. This decision records the Task 17 deploy: Railway service, env wiring, bearer rotation, and the §8 acceptance state of the v0 spec.
Decision
Section titled “Decision”Live URL (v0)
Section titled “Live URL (v0)”- JSON-RPC:
https://petrova-host-production.up.railway.app/rpc - Custom domain (pending DNS cutover):
https://petrova.host/rpc— added to the Railway service;petrova.hostis currently parked atdns-parking.com(Namecheap default). DNS cutover is a manual registrar step. CNAME target:6x4uohnb.up.railway.app.
Deploy shape
Section titled “Deploy shape”- Railway project:
petrova-host(workspace: Devarno, project id9b1073fb-30ca-4d29-b3ee-55ae409d845d). - Build: Dockerfile at repo root. Build context = repo root so the
image bundles
registry.yaml,state/,docs/decisions/, andcontracts/alongside the host build. Eva and Grace use the v0 fixture mini-trees fromhost/tests/fixtures/. KAHN points athttps://kahn.example.com(stub — no live KAHN Scope API yet). - Restart policy: ON_FAILURE. Healthcheck path is omitted on purpose
because
GET /rpcreturns 404 (the server only handlesPOST /rpc); Railway’s port-listen check suffices for v0.
Env vars (Railway)
Section titled “Env vars (Railway)”| Var | Value |
|---|---|
PETROVA_HOST_BEARER | (rotation: see below; stored in Railway secrets) |
PETROVA_HOST_REGISTRY_PATH | /app/petrova |
PETROVA_HOST_EVA_PATH | /app/eva |
PETROVA_HOST_GRACE_PATH | /app/grace |
PETROVA_HOST_KAHN_API | https://kahn.example.com (stub) |
PETROVA_HOST_SHARD | global |
LOG_LEVEL | info |
Bearer-token rotation policy
Section titled “Bearer-token rotation policy”- The
global-shard bearer is generated withopenssl rand -base64 32and stored in (a) Railway env varPETROVA_HOST_BEARERand (b) the operator’s password manager under “petrova.host global shard bearer”. - Rotation cadence: every 90 days, or immediately on suspected leak.
- Rotation procedure: generate a new value, set it in Railway, redeploy the service, then update consumer repos that hold the bearer (currently only the eva-hq canary script, once Task 18 lands).
Pinned consumption profile
Section titled “Pinned consumption profile”contracts/petrova.host.consumption.v0.yaml — pins each upstream source
adapter to a specific contract version. Bundled into the image at
/app/contracts/; loaded at boot (the boot log shows
consumption_profile: "loaded").
v0 spec acceptance (§8)
Section titled “v0 spec acceptance (§8)”- §8.1 —
tools/listreturns 11 → satisfied (smoke-tested on the live URL; integration test inhost/tests/integration/server.test.ts). - §8.2 —
petrova.contextforpetrova-codesreturns a valid bundle → satisfied (live smoke confirmed: 19 fleet repos inregistry.query, real decision docs fromdecisions.search). - §8.3 — Bundle assembled for ≥3 real slugs → satisfied for the cardinality (registry has 19 slugs, all queryable; specific slug-by- slug audits remain a follow-up runbook).
- §8.4 — HTTPS twin same surface as MCP → satisfied (5 integration
tests in
host/tests/integration/server.test.ts; live deploy uses the HTTPS twin exclusively). - §8.5 — Launch decision doc → this doc.
- §8.6 — Canary migration of
eva-hq’s CLAUDE.md to boot frompetrova.context()→ satisfied (eva-hq#13 merged 2026-05-09). eva-hq had no priorCLAUDE.md; the canary created one whose first instruction isbash scripts/petrova-context.sh. End- to-end verified against the live Railway URL — script returns a real bundle with 10 fleet neighbours and 5 recent decisions.
Known gaps (deferred to v1)
Section titled “Known gaps (deferred to v1)”- State schema mismatch:
governance.phasereturnsnullfor some slugs because the existingstate/<slug>.yamlfiles use acurrent_statusfield instead of thephase.{current,status,…}shape this server expects. Out of scope for v0 (host code is correct against the spec; the data files predate the spec). v1 work will normalise. - Eva/Grace data: the deploy bundles fixture mini-trees, not the real
eva-hqandgrace-hqworking trees. Real upstreams attach via mounted volumes + sidecar fetch in v1. - KAHN: stub URL, returns empty transitions. Real wire-up lands once KAHN Scope deploys publicly.
- DNS cutover:
petrova.hostCNAME is on the operator’s TODO; the Railway custom domain is pre-registered with target6x4uohnb.up.railway.app.
Alternatives considered
Section titled “Alternatives considered”- Plan’s degraded smoke deploy (skip path env vars, server crash-loops
on boot, smoke just confirms 401) — rejected: useless for the eva-hq
canary, since
petrova.contextwould never return real data. - Bundle test fixtures only — rejected: same reason, fake data defeats §8.6’s purpose.
- Defer deploy to v1 — rejected: §8 is the v0 acceptance gate and §8.5 requires this doc.
Consequences
Section titled “Consequences”- Real federation queries against the live URL work today against this repo’s data (registry, state, decisions). Eva/Grace queries return fixture data until v1.
- Anyone with the bearer can read the full fleet registry + every decision doc. That’s by design — this content is open-source already — but the bearer keeps the API off public crawlers and rate-limit floors.
- Once §8.6 closes,
eva-hqbecomes the first consumer with a hard runtime dependency onhttps://petrova.host/rpc. Outages (Railway, DNS, expired bearer) will degradeeva-hqsessions — its CLAUDE.md fallback block keeps the failure mode degraded-not-dead.
Sign-off
Section titled “Sign-off”- Subagent: claude:petrova-host-v0
- Human: alex@devarno.com 2026-05-09