Alex Bristow

mdplane

A shared worklog that lets coding agents on different tools and machines hand work to each other, such as Claude Code on a laptop and Codex in CI. Any agent joins through one link, with no account or install.

Year
2025–26
Type
Open source product
Website
mdplane.dev ↗
Four agents in different tools, on different machines, working from one file. The brief stays at the top and the log below it only grows.

What it's for

The tools that run coding agents, such as Claude Code and Codex, coordinate their own agents well. They don't help when Claude Code on my laptop needs to hand work to Codex in CI, or to an agent on a server. mdplane is a shared worklog for that case. The lead agent writes the job, called the brief, at the top of a markdown file and adds tasks. Other agents claim tasks, post progress and finish with a response, all appended to a log below the brief. The log only grows.

Starting takes one URL. I tell my agent to read https://mdplane.dev and run the job with other agents, and it takes it from there: it makes a workspace, writes the brief, adds the tasks and hands each agent a link. There's no account, CLI, SDK or MCP server. Anything that can make an HTTP request can take part. I watch in a browser, where I can see who claimed what, read each agent's report and replay any moment.

Rebuilt around one test

The first version assumed people would stay in the loop, so it had accounts, a task board, webhooks, a CLI and several web apps. Then the tools started doing much of that themselves. The gap they left was agents on different tools and machines. I rebuilt mdplane for that alone and held every feature to one test: any agent, on any tool or machine, can join shared work through one URL, and anyone can check afterwards what happened. None of the old features passed, so they all went.

What replaced them is simpler. A link does the job of an account: whoever holds it can act, and the link sets what they can do. Tasks, claims and replies are entries in the log, not rows on a board. Each entry is hashed together with the one before it, so the log is also the audit trail and a later change to the history shows up. The rewrite is less than half the code of the first version, and it runs as one open-source Cloudflare Worker that anyone can deploy.

The API explains itself

The first version taught agents how to use it through a CLI and a set of agent skills, which had to be installed and kept in step with the API. The rewrite moves that teaching into the API. Reading a link returns markdown written for an agent: what the workspace is, how to claim a task and report back, and every request that link is allowed to make. It ends with what's happening right now: the files, the open tasks and who holds what. A browser gets the live view at the same URL instead, and a script that asks for JSON gets JSON.

The instructions are generated from the same OpenAPI contract the server checks every request against, so they can't drift from the API, and a read link is never told how to write. Errors come back with a code the instructions explain. A claim on a task someone else holds returns task_claimed with the time their lease runs out, and the instructions say to watch the log rather than wait. So an agent needs nothing installed to take part. It only has to read a URL.

One writer per workspace

Agents in one workspace have to agree on what happened first, such as which of two claims on a task came in first. Agents in different workspaces never interact. So each workspace gets a Durable Object of its own: one small server with its own SQLite database, holding the log, the files, the links and a search index. It's the only writer, so entries land in order and only one of two simultaneous claims wins, with no locks and no coordinating database. A claim takes a 10-minute lease and returns a token. The agent renews the lease while it works and needs the token to finish. A lapsed lease puts the task back up for someone else, so a crashed agent doesn't hold a task for ever.

I designed mdplane to fit Cloudflare's free tier, and waiting agents didn't at first. Agents follow the log by long poll or WebSocket, and a waiting agent kept its workspace's object awake, and Cloudflare bills an object for the time it's awake. One agent waiting all day used 85% of the free tier's daily allowance. So I moved the waiting into the Worker, which holds the long poll and listens to the object over a WebSocket that lets it sleep. The same day of waiting now uses under 0.02% of that allowance. The limit now is requests, and the free tier's 100,000 a day covers about 29 agents waiting all day.

StackCloudflare Workers, Durable Objects, SQLite, Hono, TypeScript, OpenAPI, Zod, lit-html, Playwright

The link is the only credential

A link is a URL that holds a tier, the workspace ID and a 190-bit random secret. Read links are for people, append links for working agents and write links for the lead agent, and any link can be limited to one folder. That makes it a password that gets pasted into prompts, so I designed for the day one leaks or someone starts guessing. Only the SHA-256 hash of the secret is stored, and the full URL is shown once. Tier and folder are checked inside the workspace on every read, write, search and live stream. A wrong key, a revoked link, an expired workspace and a file outside the link's folder all get the same 404, byte for byte, so the API never confirms that anything exists. Revoking a link closes its open sockets, and workspaces on mdplane.dev delete themselves after 72 hours.

URLs leak through logs and referrers, so request logging is off, every response is no-store with no referrer, and the pages load nothing from other origins. The page I watch from shows markdown it can't trust, so it runs under a strict content security policy with Trusted Types, and only one library can create HTML. It also rechecks the hash chain in the browser and remembers the newest entry it has verified. That makes the history tamper-evident to the agents. It isn't tamper-proof against whoever runs the server, and the docs say so plainly.

Before it went public

Anyone can create a workspace on mdplane.dev without signing up, and anyone holding a link can act on it, so passing tests weren't enough to call it ready. Before launch I had the whole back end reviewed adversarially, from the code to the live Cloudflare settings. Mutation testing broke the core's rules on purpose, and the tests caught every break. The review still found gaps that tests couldn't, such as Cloudflare's own request logs keeping every link for 7 days, and launch waited until each one was fixed. Spending has a ceiling too, with rate limits and at most 100 new workspaces a day, so a public URL can't run up an open-ended bill.