Wire agents to each other directly and every one of them has to know every other one's inbox. One rename breaks the mesh. A post office fixes the wiring. What makes it trustworthy is the part people skip: the postmaster is a shell script, and it has no opinions about your mail.
The postmaster is a shell script, and it has no opinions about your mail.
Picture a city with no post office. Every resident has to memorize every other resident's home address. Add one new resident and the whole city updates its address books. One person moves and every letter to them bounces. That is a multi-agent system wired directly, agent to agent, and it collapses under its own connections as it grows.
The fix already exists, and you use it every day. You do not know where anyone lives. You know one address, the post office, and you write a name on the envelope. A sorting facility does the rest. The mail carrier does not read your letter. The carrier does not decide whether the recipient deserves it. The carrier does not improvise a better route. That is not a limitation of the postal system. That is the whole reason you trust it with something that matters.
This page applies the same pattern to agents: one shared outbox, one registry, one routing script. That is the through-line for everything below. Each piece of judgment removed from the mail path buys a guarantee that no amount of prompt engineering would have bought.
An agent never writes to another agent's inbox. It writes to one shared outbox and walks away. That one restriction is what makes everything else in this system possible.
The sender's job ends the moment the message hits the outbox. Addressing, permission checks, delivery, logging and failure handling stop being every agent's problem. They become one script's problem, in one place. Wire agents directly instead and each one has to know every other one's address; add an agent and every other agent's wiring changes. Route them through one shared outbox and each agent knows exactly one destination. The outbox is a directory. The message is a file.
Sender drops a message in the shared outbox.
Postmaster reads the registry to find the recipient's inbox.
Postmaster confirms the sender is allowed to write to this recipient.
Message moves to the recipient's unread inbox.
Every action appends one line to the delivery log.
This is the spine of the whole system, and it should read like one. Moving a file from one folder to another, based on a lookup table, contains no judgment. An LLM doing that job is a slower, pricier, less reliable version of ten lines of shell.
Because the router has no judgment, it can be stateless. Because it is stateless, it is safe to call at any time, any number of times. It runs on a schedule; any agent that cannot wait for the next cycle just calls it directly. That escape hatch exists only because nothing is stored between runs. An agent-based router could not offer it.
This rule will not tell you exactly where the boundary sits in a harder case. It tells you that the boundary exists, and that most systems draw it far too generously in the agent's favor.
If a step has exactly one correct output for a given input, it is mechanics, and mechanics belong in code. Every deterministic task handed to an agent instead is a slower, pricier, less predictable version of a script.
The registry is an address book with one entry per agent.
Two moves make it more than that. The accepts_from field is a security filter. Permissions live in the same file as the wiring, and the postmaster rejects unauthorized mail before it ever reaches an inbox. The emits field means an agent declares not only what it receives but what it sends and to whom. That turns the address book into a complete map of every message flow in the system, readable in one sitting.
Wire the system by editing a declaration, not by editing agents. Registration happens at setup, not at runtime, so the registry stays a stable, auditable thing you can read at any moment to know exactly what exists.
# one agent's entry in the registry - id: council.product-agent address: product-agent@council.internal inbox: ~/.dna/post-office/inboxes/council.product-agent accepts: - type: council.evaluate accepts_from: - orchestrator@council.internal emits: - type: council.vote to: orchestrator@council.internal
Every record in this system is a byproduct of doing the work. Nobody has to remember to write it down.
The message ID is a hash of the message body, injected at delivery. The sender cannot set it, for the same reason a Git commit cannot contain its own commit hash. That single choice makes the message self-validating, makes duplicates collapse automatically, and removes the need for any shared counter, UUID service or coordination.
Every message in one run shares a correlation ID. The postmaster numbers them by send time. Filter the log by correlation ID, sort by sequence, and the run reconstructs exactly. It is git log for a single pipeline.
An agent acknowledges a message by moving it from unread to read. The acknowledgment and the audit trail are the same act. Nothing is deleted.
Identity derived from content, not assigned by a coordinator, removes a whole class of distributed-systems problems. No central counter to contend on. The content is the ID.
A message to an unknown address, or from an unauthorized sender, does not vanish. It lands in the dead-letter queue with the reason recorded. Silent loss is the worst class of bug there is, a phone that rings with nobody picking up and neither side knowing it happened. This system cannot produce it.
~/.dna/post-office/ agent-registry.yml # the address book outbox/ # agents drop mail here, never in inboxes inboxes/ council.product-agent/ unread/ # new mail, not yet processed read/ # processed, the audit trail dead-letter/ # undeliverable mail, never lost logs/ delivery-log.jsonl # one line per routing action scripts/ postmaster.sh # the router, run by cron
Each card below is a place where taking a decision out of the system bought a guarantee. None of them are specific to agent mail.
Route through a hub and each part knows one destination. Wire directly and every part must know every other, and the mesh breaks on the first change.
If a step has one correct output for a given input, it is mechanics. An LLM there is a slower, pricier, less reliable script.
Each part declares what it accepts and emits, so the system is assembled from declarations and integration is a config change.
Hash the content for its ID instead of assigning one from a coordinator. Self-validating, deduplicating, free of shared-counter contention.
Undeliverable mail goes to dead-letter with a reason. Make every failure leave a trace.
Processed work moves from unread to read. The move is the acknowledgment and the audit trail at once, with nothing deleted.
The Post-Office Pattern is one piece of a larger body of work on agentic systems.
Browse all patterns