Railway Launch Arc · Phase 0 Deliverable

Railway Tenant-Isolation Pattern

The boundary that keeps Pantheon (fleet) and client workloads walled off on shared Railway + fleet vault. The predicate the pilot, MCP migration, and client retro-fit all enforce.

● DRAFT — ratified Phase 2 (#200) Closes #196 · PR #205 Epic #193 Class C — auth-surface / governance Owner: Proteus

0Purpose

Pantheon and client (Viska, future clients) workloads share one Railway account and one fleet vault. This spec defines the boundary so a client never rides the fleet's auth surface, secrets never bleed across tenants, and one tenant's compromise cannot reach another. Today the boundary is implicit — Viska is live, but isolation is convention, not rule. This makes it explicit, checkable, ratifiable.

In scope: Railway projects · tokens · secrets · auth · DB connections for hosted MCP + Mastra agents. Out of scope: n8n (VPS) · vault/fabric/obs (N5) · browser-debug (ride-local).

1The tenant model

A tenant is an isolation boundary, not a workload. Tenant MUST be readable from the Railway project name. One project per (tenant, service-group). Client tenants are siblings, never nested under Pantheon.

PANTHEON

pantheon-*

Shared fleet. e.g. pantheon-metis-agent. Auth principal: fleet OAuth client(s).

CLIENT

client-<name>-*

Per customer. e.g. client-viska-*. Auth principal: per-client OAuth client.

2–5The four walls

🔑 Project-scoped tokens

§2
  • Each project gets its own token. No account-wide token in any service.
  • Leak blast radius = one project, never the account.
  • Fleet RAILWAY_TOKEN (#194) is admin-plane provisioning only — never embedded in a deployed service.

🗝️ Per-tenant secrets

§3
  • Secrets scoped to the project that needs them.
  • Vault-sourced (Hades) → Railway sealed / reference vars, not UI plaintext-of-record.
  • Cross-tenant secret reuse = top isolation smell, never allowed.

🛂 Auth-surface separation

§4
  • Each tenant: own OAuth client + audience.
  • A client bearer is rejected by any fleet MCP (audience mismatch), and vice-versa.
  • No shared /mcp URL across tenants. OAuth 2.1 + PKCE per launch standard.

🗄️ Database isolation

§5
  • Each tenant connects only to its own DB.
  • Fix DATABASE_URL shadowing — declare DB var explicitly (e.g. APP_DATABASE_URL), don't rely on platform-injected.
  • Pantheon → Pantheon Supabase; client → client's own project. Never cross.

6Blast-radius acceptance test

The pattern holds iff, for a single compromised tenant (leaked token OR bearer OR service), every line is true. Every new Railway service PR runs this checklist.

Single-tenant compromise must reach NO further than:
Only that tenant's Railway project(s) — not the account, not siblings.
Attacker's bearer rejected by every other tenant's MCP (audience mismatch).
Only that tenant's secrets — no shared secret unlocks another tenant.
Only that tenant's database.
Fleet provisioning token NOT reachable from any deployed service (admin-plane, not data-plane).

Fail any line → non-conformant, does not ship (Phase-2 ratification gate).

7Current-state gap analysis

Honesty caveat: live Railway-account enumeration is blockedRAILWAY_TOKEN not in session (#194). This rests on fleet_services + audit §6/§9, not a live config dump. Conformance below is expected, verified in Phase 4 (#203).

ServiceTenantExpected vs patternVerify
viska-mimir-agent LIVEclientnaming drifts (no client-viska-* prefix); token/secret/auth scope unconfirmed#203
mimir-chat-api LIVEclientper-client Bearer secret ✓ (named); project-scope unconfirmed#203
pantheon-metis-agent pilotpantheonborn conformant — built to this doc#197
mastra-bridge MCPpantheondeploy + auth unconfirmed#201

Known drift → Phase 4 (#203), additive / no downtime: (a) Viska naming predates the prefix rule; (b) DATABASE_URL shadowing/naming-drift (audit §9); (c) live-client token/secret/auth scoping never audited.

8Enforcement — how the arc uses this

Phase / issueUses the pattern to…
1 · pilot #197build pantheon-metis-agent conformant from birth — the reference instance
2 · ratify #200sign this doc off as the standard; athena if it becomes a fleet rule
3 · migrate #202place each lifted MCP in the correct tenant project under §1–§5
4 · Viska harden #203measure live-client drift against §7 and remediate to conformance
5 · ops #204blast-radius checklist (§6) becomes a standing audit item

Review hook: any new Railway service PR runs the §6 checklist; any cross-tenant secret/token/auth reuse = CHANGES_REQUESTED.

9Open questions — resolve at ratification (#200)

  • Hostname strategy — per-tenant subdomain vs per-service? (affects OAuth issuer layout)
  • OAuth issuer — one fleet issuer w/ per-tenant audiences, or per-tenant issuers?
  • Promote to fleet rule? — if yes, athena ratification + a ~/.claude/rules/ entry.
  • Multi-client scale — does client-<name>-* scale to N clients on one account, or does a 2nd client warrant a separate Railway account? (blast-radius vs cost)