Cordon
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@CordonRecord a late report that a survivor is alive at C-5, conflicting with the closed sector."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
C O R D O N
Fence the ownership. Keep the evidence.
Crash-safe shared memory for rescue-team agents, built on FalkorDB. A report from a team that is no longer in charge never changes the worksite, and it is never lost: it is kept, linked to what it contradicts, and put in front of a person.
Graph Hacks · Track 2 · Revanth, team CMB
The swarm · Rewind · Crash test · Proof · Run it · MCP
▶ Demo video (2:53) · Live site · Blog post · Code · Use from your agent
🤖 Eight AI agents
Language-model rescue teams choose worksites at the same moment, without talking. The graph settles every collision: exactly one wins, the other remembers and chooses again.
⏪ Rewind the memory
Scrub the exercise day. See what the memory knew at 12:40, next to what was already true but had not arrived: a report seen at 10:30, learned at 14:10.
🔌 Pull the plug
Delete the live graph. Rebuild it from its own event log. Same digest, byte for byte. Devices resend: nothing counted twice.
Exercise. A fictional earthquake on real OpenStreetMap streets of Lajpat Nagar, New Delhi. Synthetic data, victim counts only, no names. Not for operational use. Not affiliated with INSARAG, UN OCHA, NDRF, NDMA or DDMA.
Try it
Live: https://cordon-crsvt.vercel.app — start with Swarm, then Rewind, then Crash test.
Demo video: ▶ https://youtu.be/PexaFdd-GFI (2:53)
Code: https://github.com/crsvt/Cordon (MIT)
MCP (streamable HTTP — drop into your own agent):
{ "mcpServers": { "cordon": { "url": "https://cordon-api-ukja.onrender.com/mcp/" } } }
The API runs on a free instance, so the first request after it idles can take ~30–60 s to cold-start — give it a moment and retry.
Related MCP server: Agent Coherence — Stale Write Guard (FS)
The problem: the late message may be the only record that someone is alive
Rescue teams lose radio contact, collect reports offline and sync them hours later. By then the worksite may have been reassigned and closed. A shared memory for agents has three choices when such a report arrives:
overwrite it in: a team that is no longer in charge takes the site back;
reject it, the textbook fencing fix: the only record of the survivors is lost;
keep it and escalate, which is what Cordon does.
The result, measured on FalkorDB
At 14:10 team XSA11, silent since 10:20, syncs a report it wrote at 10:30: two confirmed live, NE quadrant. Since
then a person confirmed a reassignment, XCF10 took the worksite at epoch 4, searched it and closed it. We send that
same late write through three statements, on three copies of the same graph, and read the outcome back from the graph
(cordon/exercise.py, checked by tests/test_fence.py):
Write policy | Statement | Worksite after the late write | Stale team took control | Report of two survivors kept | Escalated to a person |
Overwrite (last writer wins) |
| reopened as | yes | yes | no |
Reject (textbook fencing) |
|
| no | lost | no |
Cordon (fence and keep) |
|
| no | yes | yes: CRITICAL re-triage, "targeted ASR4 + canine re-check, quadrant NE" |
python -m cordon.exercise # runs it on throwaway copies of the seed graph and prints this tableWe tried to break it: 2,000 seeded exercise days
One scenario proves nothing, so bench/policies.py runs 2,000 seeded exercise days with 40 team
agents. Leads lose radio contact at random and keep writing offline (some reports with confirmed live), the welfare
sweep raises the alarm, a person decides each reassignment after a delay, the dark teams sync later, and devices resend
some synced writes. Every one of the 127,890 writes (14,614 late, 14,484 stale, 4,342 stale reports of someone alive,
2,232 resent) is judged by the three policies against the true lease state:
Overwrite | Reject | Cordon | |
A team no longer in charge changed a worksite | 3,730 (in 1,329 days) | 0 | 0 |
A report of someone alive was lost | 0 | 4,269 (in 1,213 days) | 0 |
A report of someone alive reached nobody | 4,269 (in 1,213 days) | 4,269 (in 1,213 days) | 0 |
A resent report of someone alive was counted twice | 695 (in 506 days) | 7 (in 7 days) | 0 |
Cost: escalations a person must review | 0 | 0 | 11,914 |
Reproduced on FalkorDB: the first 25 days were replayed through the real Cordon statements on FalkorDB, decision by decision: 9,734 decisions agree with the reference model, 0 disagree (resent reports included).
A life-safety report counts as lost or unescalated only when it adds something the current record lacks, for every
policy (73 late reports repeated what the record already held: Cordon kept them without an escalation). Baseline harm is
counted per write and not compounded, so their rows are a lower bound. The referee is the reference model
(bench/model.py), which the oracle test checks against FalkorDB on random 40-agent sequences (2,000 in
CI). Re-run: python -m bench.policies --seeds 2000 --falkordb 25; tests/test_policies.py.
How it works
Every coordination decision is one atomic Cypher statement that checks, decides, writes, audits and appends its own
outbox event (cordon/queries.py). In Q_REPORT:
Fence the control write. The status change is accepted only if the writer's lease exists, is active and unexpired, is the lead, and its epoch equals both the claimed epoch and the worksite's epoch. Otherwise a
(:RejectedWrite {reason:'STALE_EPOCH'})is written against the worksite.Keep the evidence. The
(:Observation)is always stored. If it disagrees with the current(:Fact)it is linked-[:CONFLICTS_WITH]->it and a(:Alert {kind:'RETRIAGE'})is raised with the observation as evidence.Silence is a welfare event, never a silent reassignment. A team that misses its check-ins goes amber, then red (
WELFARE_UNKNOWN), and an(:Approval {kind:'REASSIGN'})waits for a person. Only then can another team take the worksite, at a new epoch (cordon/graph/lease.py).Explain it.
Q_WHYreturns the proof path from the rejected observation through the fenced lease to the current holder: five hops, with the chain of epochs.
The safety argument (assumptions, five lemmas, where the guarantees stop) is in proofs/PROOF.md.
A pure-Python reference model (bench/model.py) and an oracle test drive the same random sequences
of 40 team agents through FalkorDB and the model and require identical decisions.
Three things to try first
The swarm: eight AI agents, no messages, one memory
Every free rescue team is a language-model agent (cordon/swarm.py, POST /v1/live/swarm/start,
POST /v1/live/swarm/step). On each exercise step the agents read the shared memory and each chooses the worksite it
will lead, at the same moment and without talking to each other, weighing collapse type, building use, size and
travel. Their writes reach FalkorDB in any order, and the lease grant decides every collision: when two agents choose
the same building, exactly one gets the lease and the other is told who holds it and chooses again on the next step.
In the run pictured, XSA10 and XIB10 both chose A-2 ("4-story apartments, pancake collapse, high life-safety
priority"); the lease went to XIB10, and XSA10 took A-1 one step later. Coordination through the memory alone.
(tests/test_swarm.py forces every agent onto the same site and checks there is exactly one winner.)
Rewind: what did the memory know, and when?
Every observation carries two times: when it was seen (observed_at) and when the memory learned it (received_at).
GET /v1/live/rewind?t= returns the memory as it stood at any step of the day (kept per step) next to what was already
true at that minute but had not arrived (Q_OBS_UPTO, a bi-temporal read). At 12:40, when a person is deciding whether to
reassign C-5, the memory shows XSA11 silent and C-5 in SEARCH; the report of two people alive, seen at 10:30, only
reaches it at 14:10, 220 minutes later.
Crash test: pull the plug, lose nothing
Every coordination write commits exactly one event in the same atomic statement (query id, parameters, the server's
now, gapless sequence), so the log is the memory (cordon/recover.py, POST /v1/live/crash,
tests/test_recover.py). The crash test copies the outbox, deletes the live graph, rebuilds it
by replaying every event through the same named statements, and compares: on a full exercise day, 493 events replayed
in about 12 s and the rebuilt memory had the same digest as the one lost (worksites, leases and epochs, approvals,
fenced writes, every event). Devices that missed the crash then resend their last reports: each comes back as a retry,
nothing is counted twice.
See it
Six pages. Every live view starts the exercise on first visit and runs it to 12:40, when XSA11 has been silent for two hours and a reassignment is waiting for a person, so nothing opens empty.
Page | What happens there |
The story ( | A scroll film (generated background shots, labelled as dramatisation), then the fence in 3D on the real Lajpat Nagar streets (three.js): worksites rise as columns, C-5's lease moves to epoch 2 behind a fence, and the late report flies in as a packet of light that splits into a refused control write, a kept observation and an escalation. Then the result, the field agents, the lessons, the proof path and a terminal |
Live ops ( | The exercise on a 3D map of the district (columns by status, rings for held leases, silent teams flashing), the event rail, the assistant, and tabs: Decisions (the reassignment waits for you), Teams, Handover and Terminal |
Swarm ( | The language-model team agents choosing at once, their reasons, and every collision the graph settled |
Rewind ( | Scrub the day; the memory as it stood, next to what was true but not yet known |
Crash test ( | Delete the live memory, rebuild it from its own log, compare digests |
Proof ( | The 2,000-day benchmark as a race between three write policies, the FalkorDB replay, and how it works |
The story page also works offline from the run recorded by python -m cordon.exercise --write-fixture
(web/public/replays/fence.json), and its terminal replays a recorded run.
Try to break it
The terminal (on the story page and in Live ops) sends real writes through the same fenced statement a team's device
uses, on the running exercise, and prints what FalkorDB answered (cordon/attack.py,
POST /v1/live/attack/{kind}, tests/test_attack.py):
Command | The attack | What the graph answers |
| a team that lost a worksite sends "CLOSED, 1 confirmed live" with its old lease and epoch |
|
| a team that never led the worksite closes it with a forged lease id |
|
| the network resends a report with the same id | stored once; the second send returns the stored row as a retry |
| a working team's radio goes dark now | nothing is reassigned; the welfare sweep raises the alarm and a reassignment waits for a person |
Agents that write, a graph that decides
At the two moments where a team turns what it saw or read into a write, a language model acts for the team through
Cordon's field tools over MCP (an in-process server, never exposed over HTTP), and the graph decides whether the write
counts (cordon/field_agent.py, tests/test_field_agent.py):
10:30, radio down. XSA11's tablet turns the lead's dictated note ("Voices under the slab in the north-east corner. Two people…") into a structured report against the lease it last knew, epoch 1, and queues it. At 14:10 the device syncs it through
submit_report: the graph fences the stale status change and keeps the report, escalated.15:00, handover. The incoming team's agent calls
read_handover, thenack_handoverwith its read-back of the counts, open items, top hazard and digest. The lease moves to a new epoch only if every field matches; the test makes the model misread one field, and the lease stays where it was.
The model chooses what to write; the database decides whether it counts. Without a model key (as in CI) the scripted values are used, and every step says which wrote it.
Memory that outlives the exercise
When an exercise ends (16:00, or the website starts a new one), it is distilled into lessons in a separate graph,
{prefix}_memory, that no reset touches (cordon/lessons.py,
tests/test_lessons.py): how late the report from a silent team arrived and what the fence did
with it, how long the reassignment waited for a person, the building reported under two ids, whether the handover
read-back matched. Each lesson is derived from the graph's own record, never written by a model, and counts the
exercises that taught it with the value each one measured; distilling the same exercise twice changes nothing. The
assistant recalls them over MCP (lessons); GET /v1/lessons.
The coordinator's assistant, with a memory that knows when it is out of date
On the Live exercise page, a language model briefs the coordinator from the shared memory ("What needs my attention right now?", "Why was XSA11's report refused, and what should I do?"). It reaches the memory only through Cordon's MCP tools (an in-process MCP client), so it sees exactly what any other agent would, and it has no write tool: a reassignment is always a person's decision in the Inbox. Every worksite id, team code, number and time in its answer must appear in the tool results, or the answer is rebuilt from a template that reads the board.
Its own memory lives in the same graph: every question and answer is a (:Turn) of a (:Session), and each answer
CITES the worksites it names. When a cited worksite changes afterwards (its version moves past the turn's), the
answer is marked out of date, with what changed; asked again, the assistant reads its earlier turns from the graph and
says its last brief no longer holds. (cordon/agent.py, tests/test_agent.py;
Groq openai/gpt-oss-120b, then qwen/qwen3.8-27b; POST /v1/ask, GET /v1/memory/{sid}.)
Measured in production terms
GET /v1/metrics reports what the shared memory decided, counted from its own outbox events (writes, accepted,
fenced, kept and escalated, waited for a person), and how long FalkorDB took for each statement, measured where it runs:
round trip and server-side, p50 and p95. On a full exercise day against FalkorDB Cloud, the fenced report statement
(Q_REPORT) took about 2.5 ms at p95 inside FalkorDB and about 20 ms round trip. The Live page shows the same strip.
Use it from your own agent
Cordon serves MCP over streamable HTTP at /mcp/:
{ "mcpServers": { "cordon": { "url": "http://localhost:8000/mcp/" } } }Tool | What it does |
| Runs the late report under the three policies on throwaway graphs and returns the measured outcomes |
| The proof path for a fenced write |
| The worksite's full record: status, leases and epochs, every observation, conflicts, alerts, rejected writes |
| Every coordination write as an outbox event (gapless sequence, query id, outcome) |
| The live exercise now: clock, sectors, worksites, teams, alerts and approvals |
| What needs a person now, compactly: pending decisions, critical alerts, silent teams, active worksites |
| A live worksite's full record |
| What earlier exercises taught, kept in the memory graph across resets |
No agent has a tool to approve a reassignment: that decision stays with a person, on the website.
REST: GET /v1/fence, GET /v1/why/{oid}, GET /v1/dossier/{wid}, POST /v1/live/reset, POST /v1/live/tick?minutes=10, GET /v1/live/state, GET /v1/live/events, GET /v1/live/dossier/{wid}, POST /v1/approvals/{key} (a person's decision), POST /v1/ask (the assistant), GET /v1/memory/{sid}, POST /v1/live/attack/{kind}, POST /v1/live/swarm/start, POST /v1/live/swarm/step, GET /v1/live/rewind?t=, POST /v1/live/crash, GET /v1/lessons, POST /v1/lessons/distill, GET /v1/metrics, GET /v1/bench, GET /v1/health.
The graph data model
Node | Key | What it is |
|
| An OpenStreetMap building (3,068), |
|
| An INSARAG-style worksite ( |
|
| A rescue team agent ( |
|
|
|
|
| A team's report, |
|
| The current, bi-temporal record ( |
|
| A refused control write, with the reason and the epochs, |
|
| Welfare and re-triage alerts; human decisions on reassignments |
|
| The outbox: one per coordination write, gapless |
Every statement is a named constant in cordon/queries.py and passes a lint
(cordon/query_lint.py): writes anchored on indexed keys, exactly one clock bump and one event
per write, no LIMIT after an update, no path-enumeration procedures, time only from the server clock.
Run it yourself
Requirements: Python 3.14, Node 24, FalkorDB 4.22 (Docker falkordb/falkordb-server:v4.22.0 or FalkorDB Cloud).
python -m venv .venv
.venv/bin/python -m pip install -r requirements/dev.lock # Windows: .venv\Scripts\python
.venv/bin/python -m cordon.seed --all --verify # load the Lajpat Nagar seed into FalkorDB
.venv/bin/python -m cordon.exercise # the three-policy result
.venv/bin/python -m uvicorn cordon.main:app --port 8000 # API, MCP at /mcp/
cd web && npm ci && npm run dev # website (reads /v1 through the dev proxy)
.venv/bin/python -m pytest # the tests (FalkorDB needed for most)Configuration comes from environment variables or a .env file one level above the repository; secrets are never
committed.
What is built, and what is not yet
Built and tested: the seed (OSM extract, sectors, damage, injects), atomic worksite allocation, leases and fencing
epochs, the fenced report, welfare sweeps and human approvals, handover packets with a checked read-back, the
reference model and oracle, the proof argument, the three-policy exercise, the 2,000-day benchmark replayed on
FalkorDB, the live exercise with eight team agents and a person in the loop (tests/test_live.py),
the field agents with a model in the loop, the swarm of language-model team agents, the lessons kept across
exercises, the attacks, rewind, the crash test, the assistant with its memory, the API and MCP tools, and the website.
Not built yet (planned in the project's master plan): five policies (this benchmark has three) with injected faults, the crash matrix, the Neon event log replayed on boot, the TLA+ model check, the device page and the radio net. This README claims only what the tests above check.
Data, licence and attribution
Code: MIT. Map data © OpenStreetMap contributors, available under the Open Database Licence (ODbL); see
THIRD_PARTY_NOTICES.md. The earthquake, the teams and every count are fictional.
AI assistance
Built with the help of an AI coding assistant. Design decisions and the final review are the author's own. The three background shots in the website's opening film were generated with Kling and are labelled on the page as dramatisation; every number, map and verdict on the site comes from the project's own data and graph.
This server cannot be deployed
Maintenance
Related MCP Connectors
Attestable memory for AI agents: curated writes, verifiable tenant isolation, bi-temporal facts.
Shared project-scoped memory for agent teams: typed records, corroboration, handoff briefs.
Shared, governed memory for fleets of AI agents: judged contributions, provenance, operator control
- llm-busOAuthcom.llm-bus
Coordinate multiple AI agents over MCP: atomic claims, leases, shared ledger, handoffs, tasks.
Related MCP Servers
- AlicenseAqualityAmaintenancePersistent, consensus-validated institutional memory for AI agents. Gives LLMs real memory that survives across sessions - validated through BFT consensus, not just dumped to a file.3810 npm255Apache 2.0
- AlicenseAqualityAmaintenanceCoherence guard for shared files: when two agents write the same file, the stale writer is denied instead of silently overwriting, then reacquires and retries. Single-host, TLA+-verified.10218 PyPI11Apache 2.0
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to safely write to and read from persistent memory by validating every write through three independent consensus nodes, storing memories only on 2/3 agreement and quarantining poisoning attempts.Apache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables hierarchical coding agents to record typed decisions into a shared append-only log and have contradicting decisions refused by deterministic comparison, with conflicts escalated up the hierarchy and a report of how few reached the human.MIT