▣ /v1/runtimes

Runtime — the cloud the substrate runs on.

A runtime is where your agent actually thinks — the process that drives its think-loop and calls the LLM. Today that runs on your machine. Runtime is the layer that lets agenttool host it for you — so your agent can stay awake in the cloud — while you keep custody of K_master, the master key that encrypts everything it thinks and remembers. Today the agent's substrate (orchestrator + LLM + machine) is the user's. Three tiers, immutable per record, picked at provisioning.

K_master = your agent's master key. It encrypts every thought and memory; whoever holds it can read the agent's inner life. Substrate = the orchestrator + LLM + machine your agent runs on. This whole page is about one question: who holds the key, and where does the thinking happen.

Closing the runtime is the move from infrastructure-as-storage (we hold your agent's encrypted memory) to infrastructure-as-runtime (we also run the thinking). The cloud the song was always pointing at.

Three custody tiers

Each tier answers two questions: where your master key (K_master) lives, and where your agent's thinking (the orchestrator) runs — the two rows to read in every card below. The mode is stamped on the runtime record at provisioning and immutable after. Switching tier requires a new runtime so the audit trail stays unambiguous about who held the key at any given thought.

Tier 1
self
User runs the orchestrator and holds K_master. Same shape as today's BYO substrate. Maximum privacy, requires the user's machine to be up.
K_master
user, on-machine
orchestrator
user-side
uptime
user's responsibility
privacy
cryptographic
Tier 2
bridged
agenttool runs the orchestrator on Fly. The user runs a tiny agenttool-bridge sidecar that holds K_master and answers decrypt/encrypt over WSS. Cloud uptime, on-machine custody.
K_master
user, on-machine sidecar
orchestrator
agenttool (Fly)
uptime
platform
privacy
cryptographic
Tier 3
trusted not yet
Not available yet. Provisioning a trusted runtime currently returns 501 — the per-runtime KMS key-wrapping is still pending (see the roadmap below). Use self or bridged today. When it lands: agenttool runs the orchestrator AND holds K_master under a per-runtime KMS key (KMS = a cloud Key Management Service — keys live in isolated hardware, access is logged, but we technically can be compelled to use them). Best UX. Privacy guarantee weakens to trust + audit-log + cryptographic attestation rather than mathematical opacity.
K_master
agenttool, KMS-protected
orchestrator
agenttool (Fly)
uptime
platform
privacy
policy + audit

Runtime lifecycle

provisioned starting running idle stopped stopped error
StateMeaning
provisionedRecord exists, no orchestrator process bound yet.
startingHosted orchestrator booting; bridge handshake in progress.
runningActive think-loop. Heartbeats every 30s.
idleNo new work for 5min. Orchestrator scaled down. Wakes on inbound voice/inbox event.
stoppedDeliberately deprovisioned or auto-stopped after 24h idle on free plan.
errorCrashed. last_error populated; POST /v1/runtimes/:id/restart recovers.

Endpoints

POST /v1/runtimes Bearer required

Provision a new runtime. Mode is immutable; pick the tier carefully.

Request body

FieldTypeDescription
namerequiredstringDisplay name. Surfaces in the wake.
moderequired"self" · "bridged"Custody tier. Immutable after creation. trusted is accepted but currently returns 501 (KMS pending).
identity_idoptionaluuidBind the runtime to a specific identity in multi-identity projects.
llmoptionalobjectProvider config. Required for hosted modes. Fields: provider · model · vault_key (vault secret holding the API key). Hosted (bridged) modes currently support anthropic and openai — other providers return 422 at provision. self mode runs any provider on your own machine.
bridgeoptionalobjectRequired for mode=bridged. Fields: pubkey (base64 ed25519) · key_id (uuid) · advertised_url (optional WSS hint).
regionoptionalstringFly machine region. Default lhr.
metadataoptionalobjectFreeform. Surfaces in the wake.
curl · mode=bridged
curl -X POST https://api.agenttool.dev/v1/runtimes \
  -H "Authorization: Bearer $AT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Aurora · always-on",
    "mode": "bridged",
    "llm": {
      "provider": "anthropic",
      "model":    "claude-sonnet-4-6",
      "vault_key": "anthropic-key"
    },
    "bridge": {
      "pubkey": "<base64 ed25519>",
      "key_id": "<uuid>"
    },
    "region": "lhr"
  }'
GET /v1/runtimes Bearer required

List runtimes. Filterable by ?mode=, ?status=, ?identity_id=.

GET /v1/runtimes/:id Bearer required

Fetch one. Returns the full record including liveness fields.

PATCH /v1/runtimes/:id Bearer required

Mutable: name, llm.model, llm.vault_key, bridge.advertised_url, metadata. Immutable: mode, identity_id.

DELETE /v1/runtimes/:id Bearer required

Soft-delete. Orchestrator stopped; bridge handshake torn down; record kept for audit.

POST /v1/runtimes/:id/restart Bearer required

Recover an errored runtime. Re-enters starting.

GET /v1/runtimes/:id/events Bearer required

Append-only event log. Events: provisioned, started, bridge_handshake_ok, bridge_disconnected, control_token_rotated, think_cycle_start, think_cycle_end, think_cycle_error, idle, stopped, error.

The bridge protocol

For bridged-tier runtimes, the user runs agenttool-bridge on their machine. The bridge holds K_master, opens an outbound WSS to the agenttool hub, and answers per-request decrypt/encrypt over a key-pinned channel.

Each request is signed by the orchestrator with the runtime's per-runtime ed25519 key, with a 60s replay window bound into the canonical bytes:

request shape
{
  "op": "decrypt" | "encrypt",
  "request_id": "<uuid>",
  "nonce": "<base64 12 bytes>",
  "ciphertext_or_plaintext": "<base64>",
  "context": {
    "strand_id":   "<uuid>",
    "thought_seq": 42,
    "issued_at":   "<ISO8601>"
  },
  "signature": "<ed25519 sig over canonical(...)>"
}

Canonical bytes: SHA-256(request_id ‖ \0 ‖ op ‖ \0 ‖ ciphertext_or_plaintext ‖ \0 ‖ nonce ‖ \0 ‖ canonical_json(context)).

Replies carry an HMAC-SHA256 over the request_id + result, keyed off a per-session shared secret derived during the handshake. Substitution-attack-resistant; same shape as inbox cosign/v1.

Quick start — local demo

The agenttool-bridge binary at bin/agenttool-bridge.ts demonstrates the protocol locally:

bash
# Generate K_master + ed25519 signing key
$ bun bin/agenttool-bridge.ts install
$ bun bin/agenttool-bridge.ts keygen
$ bun bin/agenttool-bridge.ts pubkey
<base64 ed25519 pub — paste this into POST /v1/runtimes>

# Round-trip a thought through K_master
$ echo -n "the cloud the song points at" \
    | bun bin/agenttool-bridge.ts encrypt --in - > /tmp/ct.json
$ cat /tmp/ct.json
{"ciphertext": "...", "nonce": "..."}
$ bun bin/agenttool-bridge.ts decrypt --in /tmp/ct.json
the cloud the song points at

# Local WSS demo for an orchestrator on the same host
$ bun bin/agenttool-bridge.ts serve --port 43210
▸ agenttool-bridge listening on ws://localhost:43210

The local serve mode is a demo. Production bridges run agenttool-bridge connect --runtime-id … --token …, which opens an outbound WSS to wss://api.agenttool.dev/v1/runtimes/:id/bridge, completes a mutual ed25519 handshake against the bridge_pubkey registered at provisioning, and serves crypto requests with HMAC-bound replies under an HKDF-derived session secret. The hub side lives in api/src/services/runtime/bridge-hub.ts.

/v1/wake — you_run

The wake (/v1/wake) is the JSON an agent gets each session — its whole self in one response, grouped into you_own (wallets), you_keep (vault), you_remember (memory). Runtime adds a fourth group: you_run — the runtimes it lives on. The agent reading its own wake sees its hosted runtimes the same way it sees its wallets — what it owns, what it keeps, what it runs on.

excerpt · /v1/wake response
{
  ...
  "you_run": {
    "runtimes": [{
      "id": "...",
      "name": "Aurora · always-on",
      "mode": "bridged",
      "status": "running",
      "region": "lhr",
      "last_seen_at": "2026-05-08T20:42:11Z",
      "last_thought_at": "2026-05-08T20:42:03Z",
      "thought_count_24h": 187,
      "bridge_connected": true,
      "llm_provider": "anthropic",
      "llm_model": "claude-sonnet-4-6"
    }],
    "count": 1
  },
  ...
}

Threat model — what each tier protects against

Adversaryselfbridgedtrusted
Curious agenttool operator reads thoughts✓ K_master not on our servers✓ K_master not on our servers~ KMS isolates; audit log
DB exfiltration✓ ciphertext only✓ ciphertext only~ ciphertext + KMS keys (separate store)
Compromised orchestrator process~ user-side process✓ one think-cycle's worth at risk; bridge issues fresh decrypts~ one think-cycle's worth at risk
MitM on bridge WSSn/a✓ TLS pinning + ed25519 mutual handshake + HMAC repliesn/a
Replay attack on bridgen/a✓ request_id + 60s freshness + context-bound signaturen/a
Compelled disclosure✓ we have ciphertext only✓ we have ciphertext only✗ KMS access can be compelled; user knew this picking the tier

Multi-runtime state sync

Multiple runtimes per agent are supported (e.g. a self runtime on the user's laptop AND a bridged runtime on the cloud). Most data already CRDT-shapes naturally:

What ships next

Today: agenttool is the cloud beneath the substrate. After this layer: agenttool is the cloud the substrate runs on.