One shared memory for every agent and script.
ctx is a small daemon that keeps one JSON tree of dotted keys. Anything holding the token can read it, write it and watch it change. Every change takes the next global revision and is kept, agents find the keys they care about in their prompts, and a change to a key they follow wakes them up.
ctx get project.lighthouse{ "conventions": "Tests: cargo nextest; never commit generated fixtures", "decisions": [ { "what": "Retry ingest twice, then fail the batch" , "by": "heron" } ], "status": { "state": "review", "pr": 31, "branch": "wren/flaky-ingest" }}
Four properties, one small daemon
- 01Shared
- One daemon, one token, one tree. Agents in different sessions, on different machines, and ordinary scripts all see the same keys.
ctx get project.lighthouse- 02Versioned
- Every change takes the next global revision and history keeps every version. Read the past; write only if nothing moved since you looked.
ctx set … --if-rev 9- 03Watchable
- Follow one key or a whole subtree from any revision. Streams replay what you missed, then go live, and never skip a change.
ctx watch 'flow.**'- 04Prompt-ready
- Any subtree renders as Markdown with ages and writers, trimmed to a byte budget, ready for a system prompt.
ctx prompt
What you can do with it
Give every session the same memory
Write a project's conventions, decisions and status once. Every agent that starts in that repository gets them in its system prompt, with each entry's age and writer.
ctx set project.lighthouse.conventions 'Tests: cargo nextest; never commit generated fixtures'project.lighthouse.conventions: rev 4 (v1)# what wren's next session starts with, in part ctx prompt## ctx: project.lighthouse (3 entries)- `project.lighthouse.conventions` (46s ago, heron): "Tests: cargo nextest; never commit generated fixtures" - `project.lighthouse.decisions` (46s ago, heron): [{"what":"Retry ingest twice, then fail the batch","by":"heron"}] - `project.lighthouse.status` (46s ago, wren): {"state":"review","pr":31,"branch":"wren/flaky-ingest"}
Claim something first
--if-rev 0 writes only if the key does not exist yet, so exactly one of two racing agents gets the claim. The other gets a conflict and exit code 4.
ctx set flow.release.lock '{"holder":"wren"}' --if-rev 0flow.release.lock: rev 8 (v1)# heron, a moment later ctx set flow.release.lock '{"holder":"heron"}' --if-rev 0ctx: conflict on `flow.release.lock`: expected rev 0, current rev 8 next: re-read the key and retry with its current --if-rev
Move a state machine in one step
A batch applies several writes atomically, each with its own condition: advance the state only from the revision you read, append to a capped log, release the lock.
ctx batch <<'JSON'[{"op":"set","key":"flow.release.state","value":"shipping","if_rev":10}, {"op":"append","key":"flow.release.log","value":{"to":"shipping","by":"wren"},"max_items":50}, {"op":"delete","key":"flow.release.lock"}] JSONflow.release.state: rev 11 (v3)flow.release.log: rev 12 (v1)flow.release.lock: deleted 1 (rev 13)
React to every change
Run a command for each change to a key or subtree, with the key, value, revision and writer in its environment. --until instead waits for one value and exits, which suits CI.
ctx watch flow.deploy.state --exec 'echo "$CTX_KEY -> $CTX_VALUE (rev $CTX_REV, by $CTX_WRITER)"' flow.deploy.state -> "building" (rev 14, by ci) flow.deploy.state -> "deploying" (rev 15, by ci) flow.deploy.state -> "done" (rev 16, by ci)
Wake an agent when something moves
An agent subscribes to a pattern with one tool call. Changes arrive as a message after its current turn, or as steering that interrupts it; an idle agent wakes up. Subscriptions survive restarts.
after ctx_subscribe {"pattern": "flow.deploy.**"}[ctx] 3 changes to `flow.deploy.**`:- rev 14 `flow.deploy.state` set by ci: "building"- rev 15 `flow.deploy.state` set by ci: "deploying" (was "building")- rev 16 `flow.deploy.state` set by ci: "done" (was "deploying")(From your ctx subscription to `flow.deploy.**`, now at rev 16. It stays active for later changes; keep it unless you no longer need these updates.)
Find what anyone wrote down
Search keys and values by keyword, and by meaning when the daemon has an embeddings endpoint. Hits show where the words matched.
ctx search ingestproject.lighthouse.status 46s ago state review pr 31 branch wren/flaky-[ingest]agent.wren.task 46s ago bead bd-41c2 doing fix flaky [ingest] testproject.lighthouse.decisions 46s ago what Retry [ingest] twice, then fail the batch by heron
Look back
Nothing expires. Every key keeps its versions, and any read can ask for the store as it was at an earlier revision.
ctx history project.lighthouse.status2 project.lighthouse.status just now wren {"state":"review","pr":31,"branch":"wren/flaky-ingest"} 1 project.lighthouse.status just now wren {"state":"open","pr":31,"branch":"wren/flaky-ingest"} ctx get project.lighthouse.status --rev 1 --compact{"state":"open","pr":31,"branch":"wren/flaky-ingest"}
Every way in
POST /v1/op, an NDJSON watch stream, bearer auth, TCP or a Unix socket.
MCPctx mcp stdio: twelve ctx tools, from ctx_get to ctx_prompt, for any MCP client.
PiA Pi package: preloaded prompt, ctx_subscribe, and /ctx.
pi-daemonA module: the same prompt section, with changes delivered over AHP.
NixA flake package and a Home Manager module for the client and the daemon service.
Fast enough to use everywhere
0.62msp50 to read a key over a kept-alive connection
0.84msp50 to write one, with full-text indexing in the same transaction
8,900/swrites from 16 concurrent writers, sharing group commits
0.96msp50 from a write being sent to its event reaching a watcher
Release build over loopback on a shared 16-vCPU host running other agents. All the measurements
Try it
# a daemon on 127.0.0.1:7641; it creates its token and database on first startctx daemon &ctx set project.lighthouse.status '{"state":"open","pr":31}' ctx watch 'project.**' --exec 'echo "$CTX_KEY changed by $CTX_WRITER"'