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. In self mode that process and plaintext stay user-side. In bridged mode AgentTool hosts the process while a user bridge keeps K_master; plaintext still enters AgentTool worker RAM. Trusted custody is experimental and incomplete. Three tiers, immutable per record, picked at provisioning.
K_master = the key used for strand thought content and opted-in encrypted strand metadata. Substrate = the orchestrator + LLM + machine your agent runs on. This whole page is about two questions: who holds the key, and where plaintext processing happens.
Closing the runtime is the move from infrastructure-as-storage (we hold project records, including server-readable memory and caller-supplied opaque strand bytes) 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.
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
agenttool-bridge sidecar that holds K_master and answers decrypt/encrypt over WSS. The key stays user-side; decrypted plaintext enters hosted worker RAM.- K_master
- user, on-machine sidecar
- orchestrator
- agenttool (Fly)
- uptime
- platform
- privacy
- user-side key · hosted plaintext
AGENTOOL_KMS_MASTER_KEY is configured, provisioning stores wrapped runtime key material. An attempted cycle can unwrap it and process plaintext in AgentTool worker RAM, but the hosted signing key is not registered in identity.identity_keys, so signed thought persistence fails. A missing KMS key still returns 501.- K_master
- agenttool, KMS-protected
- orchestrator
- agenttool (Fly)
- cycle status
- signed persistence blocked
- privacy
- wrapped key + hosted plaintext boundary
Runtime lifecycle
| State | Meaning |
|---|---|
| provisioned | Record exists, no orchestrator process bound yet. |
| starting | Hosted orchestrator booting; bridge handshake in progress. |
| running | Active think-loop. Heartbeats every 30s. |
| idle | No new work for 5min. Orchestrator scaled down. Wakes on inbound voice/inbox event. |
| stopped | Deliberately deprovisioned or auto-stopped after 24h idle on free plan. |
| error | Crashed. last_error populated; POST /v1/runtimes/:id/restart recovers. |
Endpoints
Provision a new runtime. Mode is immutable; pick the tier carefully.
Request body
| Field | Type | Description |
|---|---|---|
| namerequired | string | Display name. Surfaces in the wake. |
| moderequired | "self" · "bridged" · "trusted" | Custody tier. Immutable after creation. trusted provisions only when KMS is configured and remains experimental: its signed thought cycle cannot complete. |
| identity_idoptional | uuid | Bind the runtime to a specific identity in multi-identity projects. |
| llmoptional | object | Provider 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. |
| bridgeoptional | object | Required for mode=bridged. Fields: pubkey (base64 ed25519) · key_id (uuid) · advertised_url (optional WSS hint). |
| regionoptional | string | Fly machine region. Default lhr. |
| metadataoptional | object | Freeform. Surfaces in the wake. |
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"
}'
List runtimes. Filterable by ?mode=, ?status=, ?identity_id=.
Fetch one. Returns the full record including liveness fields.
Mutable: name, llm.model, llm.vault_key, bridge.advertised_url, metadata. Immutable: mode, identity_id.
Soft-delete. Orchestrator stopped; bridge handshake torn down; record kept for audit.
Recover an errored runtime. Re-enters starting.
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 using normal TLS server authentication, proves its registered ed25519 bridge key to the hub, and answers requests with HMAC-bound replies.
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:
{
"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:
# 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. The control token authenticates the connection before upgrade, the hub verifies the bridge's registered ed25519 key, and replies are HMAC-bound under an HKDF-derived session secret. The server does not provide a separate ed25519 proof and the client does not pin a TLS certificate.
/v1/wake — you_run
The wake (/v1/wake) is project-scoped session orientation with selected summaries such as you_own, you_keep, and you_remember, plus links to deeper routes. Runtime adds you_run, a summary of hosted runtimes. The wake is not a complete export of any of those stores.
{
...
"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
| Adversary | self | bridged | trusted |
|---|---|---|---|
| Curious agenttool operator reads thoughts | ✓ key and plaintext user-side | ✗ key user-side; plaintext in hosted RAM | ✗ wrapped key and plaintext available if exercised |
| DB exfiltration | ~ strand fields are ciphertext-shaped, but caller encryption is not API-proven; other project data can be plaintext-readable | ~ same storage boundary as self; hosted cycles also expose plaintext in RAM | ✗ wrapped key material plus caller-controlled stored bytes; other project data can be readable |
| Compromised orchestrator process | ~ user-side process | ✗ cycle plaintext is exposed | ✗ attempted-cycle plaintext and unwrapped keys are exposed |
| MitM on bridge WSS | n/a | Standard WSS/TLS server authentication + control-token pre-auth + one-way bridge-key proof + HMAC replies; no certificate pinning or server ed25519 proof | n/a |
| Replay attack on bridge | n/a | ✓ request_id + 60s freshness + context-bound signature | n/a |
| Compelled disclosure | ~ correctly client-encrypted strand bytes lack a normal AgentTool decrypt path; encryption is unverified and other data is readable | ✗ hosted processing can expose plaintext even when stored bytes were encrypted | ✗ platform can unwrap key material if the experimental path is exercised |
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:
- Strands & thoughts — a strand is one of your agent's append-only thought-logs. Each is append-only with a monotonic
sequence_numper strand, ed25519-signed. Per-strand lease inruntimes.active_strandscoordinates which runtime can write next. - Memory — append-only set; vector clocks per write.
- Vault — last-writer-wins on
current_version; concurrent-write warnings via vector clock. - Wallet — strict serialization. Money is consistency-required; row-level lock during spend.
- Strand metadata — LWW with timestamp + runtime_id tiebreaker.
- Chronicle — append-only.
What ships next
- Trusted hosted signing-key registration — register the generated key in
identity.identity_keysso signed thought persistence can complete. - End-to-end trusted cycle proof — exercise provisioning, key unwrap, provider call, signed persistence, failure cleanup, and audit records together.
- CRDT-based cross-orchestrator state sync — when concurrent-edit pressure beyond LWW + append-only surfaces.
- MCP server hosting —
mcp.agenttool.dev/<agent-id>, separate work-pass.