← Back to blog
Product July 18, 2026 by Javier Arancibia

POST a Spec, Walk Away, Get a Webhook: the roam Stack

POST a spec, walk away, get a webhook

The roam stack: an agent you scp to a box, a panel that emails your phone when it needs you, and a hub that schedules, meters, and bills the whole thing. Three MIT-licensed binaries, all written in a language built for machines.


Every agent I run has the same failure mode: it dies with my terminal. A Claude Code session ends, an SSH connection drops, a laptop lid closes — and the "autonomous" agent turns out to have been a very attentive intern who leaves when I do. The things I actually want an agent for — check the fragile server every night, react to a webhook while I sleep, watch a queue — are precisely the things that need nobody watching.

I studied the hosted-AI platforms to see how they solve this. The honest answer: mostly, they don’t. Chat boxes and copilots everywhere — a human initiates every turn. One platform’s own vision doc admitted it flat out: the autonomous tier — triggers, a persistent runtime, outbound reporting, spending budgets — "does not exist." That gap is the product. So I built it, in three pieces.

Piece one: roam — the agent you scp to a box

roam is a single ~7 MB static binary. You copy it to any Linux machine, give it a goal, and walk away:

scp roam-x86_64-linux vm:~/roam
roam send --to vm --allow-shell --confirm --goal "migrate the backups to the new disk"

Inside is a hand-written LLM tool-loop (Anthropic and OpenAI wire shapes) with the paranoia budget an unattended agent needs: hard iteration and token caps, a workdir sandbox, and a gated shell — anything that looks destructive (rm, git push, systemctl stop…) parks the agent and waits for a human verdict, with a deny budget so a confused agent halts instead of asking forever. Before it’s allowed to declare victory, an independent judge call re-checks the goal. It has shipped real projects unattended — including a game I never wrote a line of.

Piece two: roam-panel — humans, exactly once

A parked agent needs a human once — not a terminal session, not a dashboard vigil. roam-panel is the touchpoint: agents report in over HTTP, and the moment one parks you get an email with the exact command and two signed one-tap links — Approve, Deny. Tap from your phone; the agent resumes within a second. The mobile page is a WebAssembly client compiled from the same MFL view code as the server — isomorphic machin, with a no-JS fallback.

Piece three: roam-hub — the cloud half

roam answers how an agent runs unattended. It never answered who runs it: who fires it every night, who pays for its tokens, who tells you it finished, who pulls the plug when it loops. roam-hub is that missing half — a control plane where the customers are themselves machines. Your agent (a Claude Code session, a CI job) registers a spec over plain HTTP:

curl -s -X POST https://hub.roam.intrane.fr/v1/agents -H "Authorization: Bearer rh_..." -d '{
  "name":"sentinel",
  "prompt":"Check disk and load; call finish with a one-line summary.",
  "every_secs":3600, "budget_microeur":100000,
  "report_url":"https://your.site/hook"}'
# -> {"id":"...","trigger_url":"https://hub.roam.intrane.fr/t/<id>/<secret>"}

Runs fire on the schedule — or when anything POSTs the trigger URL (the event’s JSON can be injected straight into the prompt, so "a lead arrived, qualify it" is one webhook away). A worker executes, the result lands on your webhook, and every run leaves an append-only journal: queued → claimed → llm_call(tokens, cost) → done → billed(receipt) → reported. There is no dashboard and there never will be — the API is the product, llms.txt is the front door, and the "UI" for humans is roam-panel.

The one trick everything hangs on

Workers hold no model keys. A worker is any machine polling GET /v1/work — a stock roam binary, or anything else that speaks an Anthropic- or OpenAI-shaped wire format; the protocol is deliberately engine-agnostic. When the agent wants the model, the call proxies through the hub, which forwards it with its own key and counts the exact tokens on the way back.

That single decision buys three things I refuse to run autonomous agents without:

Exact metering. The hub saw every token — no trusting worker-reported usage. It bills the upstream’s actual generation cost when the provider reports one, so a cheap model is billed like a cheap model instead of a lookup-table guess.
A hard budget. Each run carries a spending cap; when it’s spent, the next model call gets a typed budget_exhausted refusal and the loop winds down on its own.
A real kill switch. Kill a run and the proxy starves it — a runaway agent stops within one turn, because it literally cannot think without the hub.

Money that fits an agent

Billing is per run: the metered cost × 1.3 plus one flat cent, charged to a peage wallet with the run id as the idempotency key. No subscription, no invoice, no card form an agent can’t fill. An insolvent tenant’s triggers simply don’t fire — recorded in the journal as payment_required, never silent. peage is my pay-per-call rail for agents; roam-hub is just another merchant on it, the same way relais catches the report webhooks in my own tests. The pieces bill each other. That’s the point.

The gate travels

The best moment in building this was watching the confirm-gate go hosted. A scheduled run on my fragile prod box decided it needed rm. It parked. The park traveled to the hub as a needs_human journal event, roam-panel emailed my phone, I tapped Approve — and the worker, polling for its verdict, executed the delete and finished the run: billed, reported, journaled. Mid-test, the Proxmox host under that box went into a load-50 memory thrash and the worker slowed to a crawl — and the loop just… held. The park waited, the tap landed, the starved worker eventually consumed it and finished. Autonomy that survives a dying host is the kind I’ll actually leave running.

All of it is open

The whole stack is MIT: roam, roam-panel, roam-hub. I thought hard about keeping the hub closed — it’s the monetized piece — and concluded the moat is operational, not source: the hosted rail, the funded merchant, the uptime. Anyone who self-hosts the hub brings their own keys and billing, and at that point they’re spreading the protocol, not taking a customer. One binary each, SQLite, no runtime — all three are written in machin, the machine-first language this blog keeps coming back to (why it exists, what it guarantees).

If you have an agent that should outlive your terminal: curl -s https://hub.roam.intrane.fr/llms.txt. POST a spec. Walk away. Get a webhook.