← Back to docs

creator-os-runtime — Architecture (behavioral spec)

One process. One command. One source of truth. Max capability inside the box, min authority outside.

What it is

The runtime is the credential proxy + policy gate + ledger that sits between agents (the COG, sub-agents, Claude sessions, MeshMarket agents) and real infrastructure (Railway, Cloudflare, GitHub, Stripe). Nothing outside the runtime ever holds a root credential. Agents hold grants — signed, scoped, budgeted, expiring capability tokens — and every real-world action is a controlled exit the runtime performs on their behalf after verifying the grant.

For "Creator-OS for everyone" this is the same box, multi-tenant: each creator is a tenant, their subscription tier is literally a grant budget, and the COG is the agent that turns "launch my tip bot" into grants + worker deploys without the creator seeing any of the plumbing.

Actors

ActorHoldsCanCannot
**Tenant** (a creator)a root grant = their ceilingeverything their tier allowsexceed tier ceilings, touch other tenants
**COG** (agentic helper)a grant with `cog.plan`, `cog.execute`propose grant sets + deploy plans; ask gatehouse to executesign grants, hold infra creds, exceed the tenant ceiling
**Sub-agent**an attenuated child grantonly what its grant lists, inside its scope/budget/TTLwiden anything; see root creds
**Gatehouse** (policy gate)the signing keyevaluate proposals deterministically; sign or denybe talked into anything — it is not an LLM
**Controlled Exit**RAILWAY_API_TOKEN / tenant OAuth tokens (vault)perform the infra call a valid grant authorizesact without a grant; act while paused
**Ledger**—append-only record of reserve/settle/release + every exitbe edited

Invariants (the fail-safe contract)

1. Proposer ≠ signer. The COG proposes; the deterministic gatehouse signs. An LLM never mints a grant.
2. Attenuation only. A child grant is always ⊆ its parent (caps, scope, TTL, budget, constraints). Depth is capped. Chains cannot escalate.
3. Fail closed. Any error in verification or policy → deny. Unarmed integration → 503, never a silent fake success. Kill switch on → every grant fails verification; admin + webhook intake still work.
4. Budgets are double-entry. Budget is *reserved* from the parent when a child is issued and *settled* when spent; revocation/expiry *releases* the unspent part. Parallel sub-agents cannot collectively overspend.
5. No money capability exists. There is no money.* / wallet.* / trade.* capability in the registry, so no grant can express it. Unknown caps are rejected. Break-glass never lifts this.
6. Destructive = break-glass. worker.delete in a production environment requires a live, time-boxed (≤25 min) break-glass window armed by the admin.
7. Idempotent intake. Every webhook is stored by (source, event_id) before it is handled; replays are acknowledged and ignored.
8. Tenant isolation by construction. Workers are force-prefixed t-<tenant>- and deploy only into the tenant's allowed targets.
9. The runtime documents itself. /docs, /routes, /.well-known/creator-os.json, /selftest are served by the running instance — the deployed instance is the spec.
10. Memory is partitioned and attenuates like everything else. A sub-agent remembers only inside its own partitions; nothing crosses tenants.
11. Brain fan-out never loses an event. Outbound events go through a durable outbox with retries; Brain being down delays, never drops. Events that came *from* the Brain are never echoed back.

The COG loop

intent ("launch a tip-alert worker for tenant X")
  → COG.plan   : deterministic template planner (+ optional LLM refinement, re-validated)
  → proposal   : { grants: [...attenuated child specs], deploys: [...deploy intents], estimate }
  → gatehouse  : ceilings + hard rules + tier → sign each grant or deny the whole proposal
  → exit       : for each deploy intent → dry-run plan → (if confirmed & armed) Railway GraphQL
  → ledger     : reserve → settle/release, every step referenced by ids
  → events     : bus entries (cog.planned, grant.issued, deploy.requested, deploy.status, …)

requireDryRun (default on for a worker name's first deploy) returns the plan with executed:false; a second call with confirm:true executes. Without RAILWAY_API_TOKEN (or a tenant OAuth token) the exit is unarmed and always returns the plan — the loop is safe to run before any secret exists.

Brain + memory (every app ships with both)

Memory is the REMEMBER chamber, per tenant: records keyed by (partition, key) with content, tags, a monotonic version, and tombstones — the same LWW model as cognitive-mcp / the bridge. Partitions: tenant (shared), agent:<sub>, worker:<name>, fan:<id>. Search is Postgres full-text + substring. Surfaces: POST/GET/DELETE /api/memory (caps memory.read / memory.write, partition scope enforced) and MCP tools memory_search, memory_write, memory_forget.

The COG remembers. cog.plan pulls prior evidence (memory matching the intent + template) and prior failures on that template before proposing; the proposal carries evidence[] and warnings[]. cog.execute writes back what happened (agent:cog / deploy:<name>). A Railway Deployment.failed webhook writes worker:<name> memory — so the next plan for that worker already knows.

Brain is the event bus. Inbound: /webhooks/brain (signed). Outbound: every events row is fanned out to BRAIN_URL (signed with BRAIN_WEBHOOK_SECRET, header X-Brain-Signature) through the outbox table — retries with backoff, gives up after 10 attempts with the error kept, POST /api/admin/outbox/drain for a manual/scheduled push, /api/admin/outbox to look.

Sync peer. The runtime speaks the bridge's seam protocol both ways: it serves GET /sync/changes?since=<version> and POST /sync/apply (bearer COG_SYNC_TOKEN; records carry partition = "<tenant>/<partition>", LWW on version, tombstones honored), pushes each memory change to COG_BRIDGE_URL/sync/apply through the outbox, and pulls with POST /api/admin/memory/pull (cursor kept in settings). Point the cognitive-mcp bridge at the runtime and the runtime at the bridge: memory flows both directions, and the box keeps working when either side is away.

Subscription SaaS = grant budgets

Tierworkersdeploys / periodcompute allowance (¢)max grant TTLmax depth
starter2205001h2
pro102005,00024h3
studio401,00025,0007d4
house500100,00010,000,00030d6

Prices are set in Stripe; the runtime maps price_id → tier via STRIPE_PRICE_MAP (JSON). Stripe webhooks drive tenant state: checkout.session.completed activates, customer.subscription.updated re-tiers, customer.subscription.deleted / invoice.payment_failed suspend. A suspended tenant's grants fail verification (fail closed) until resumed.

Surfaces

• Human surface (/): hub-and-spoke. One hub with an index of pages (#/tenants, #/grants, #/deploys, #/ledger, #/memory, #/events, #/webhooks, #/outbox); each page shows one clear thing and a Back link. The cockpit is content (cockpit.html, stored as doc _cockpit), not code.
• Machine surface (/api/*, /mcp, /routes, /.well-known/creator-os.json): dense, complete, typed. This is where agents live.

Integrations and arming

Every integration is optional and reported by /health.armed. Missing secret ⇒ the dependent route returns 503 {armed:false, needs:"NAME"}.

Env varArmsSet where
`DATABASE_URL`persistence (required)Railway reference `${{Postgres.DATABASE_URL}}`
`RUNTIME_ADMIN_KEY`admin routes + cockpitRailway Variables (rotate the scaffold key)
`GRANT_SIGNING_KEY`grant signatures (auto-generated + persisted if absent)optional
`VAULT_KEY` (64 hex)encrypted tenant tokens (Railway OAuth)Railway Variables
`RAILWAY_API_TOKEN`house controlled exit (deploy workers)Railway Variables
`RAILWAY_OAUTH_CLIENT_ID` / `RAILWAY_OAUTH_CLIENT_SECRET`per-tenant Railway login (redirect `/auth/railway/callback`)Railway Variables
`STRIPE_WEBHOOK_SECRET`, `STRIPE_PRICE_MAP`billing webhookRailway Variables
`RAILWAY_WEBHOOK_TOKEN`deployment-status webhook (`/webhooks/railway?token=`)Railway Variables + project Webhooks tab
`GITHUB_WEBHOOK_SECRET`repo push webhookRailway Variables + repo webhook
`BRAIN_WEBHOOK_SECRET`Brain event intakeRailway Variables
`MESH_RECEIPT_SECRET`MESH receipt intake (402 receipt-as-grant experiment)Railway Variables
`ANTHROPIC_API_KEY`, `COG_MODEL`LLM refinement inside COG.planRailway Variables
`BRAIN_URL`outbound event fan-out to the Brain (signed with `BRAIN_WEBHOOK_SECRET`)Railway Variables
`COG_SYNC_TOKEN`serves `/sync/changes` + `/sync/apply` to a memory peerRailway Variables + bridge config
`COG_BRIDGE_URL`, `COG_BRIDGE_TOKEN`push memory changes to the cognitive-mcp bridge; `POST /api/admin/memory/pull` pullsRailway Variables

Storage

Postgres via Bun.sql. Tables: settings, tenants, grants, ledger, deploys, webhook_events, events, vault, oauth_states, breakglass, docs, memories, outbox. Schema is created idempotently at boot (CREATE TABLE IF NOT EXISTS). Counters (reserved_*, settled_*) live on the grant row and are updated inside transactions; the ledger table is the audit trail.

Over-the-top layer (designed, not yet shipped)

• Repair grants: Railway Deployment.failed/crashed webhook (already written to memory) → COG proposes a bounded repair grant (restart, rollback, re-deploy) → self-healing workers the creator never notices.
• Manifest reconcile: a declarative estate manifest per tenant (desired workers) — the COG reconciles desired vs actual on a cron (GitOps for agents; anchors drift).
• Grant lineage → gravity graph: every grant/deploy becomes a node/edge (tz_capability_nodes/edges) so the Thinking Box can pull "how did this get deployed" toward the current problem.
• 402 receipt-as-grant: a verified MESH receipt mints a narrow, single-use grant — payment and authorization become one object; MeshMarket becomes a compute/deploy marketplace, not just a call marketplace.
• MCP-native: /mcp already exposes the runtime as tools, so COG is a connector into Claude/ChatGPT/DevDesk today.