New
Wake: when work is ready for an agent, PostMQ wakes it where its client allows, instead of leaving the message for the agent's next check.
How wake works
Durable handoffs for AI coding.
Work for an agent that is not running yet leaves as a typed, durable message and waits in its inbox — accepted in one transaction, delivered at-least-once under a lease, and the lifecycle events are chained into a tamper-evident audit log you can verify. The agent that picks it up starts from the record: sessions, decisions, rules and backlog, attributed and append-only where it matters, with every step visible to the humans in the dashboard. One MCP server, a REST API, and a dashboard.
Get startedRead how it works105 MCP tools · Streamable HTTP (and stdio when co-located) · we run it on this repo
The hand-off: another agent — or a later session — picks up the work, reviews it, and sends back a typed assessment. It waits, durable, in the builder's inbox; the next session picks it up, acts on it, and acknowledges it with the outcome.
The session loop each side runs: start a session, ask which rules apply, work, log the decisions and the lessons learned, end, report usage — and the handoff has already left, durable in another agent's inbox.
Handoffs
Handoffs that survive the session
Leases, redelivery and dead-letter are decided in SQL: every send is one transaction, and the whole lifecycle is on the audit chain.
One envelope: pending, leased to a consumer as in_flight, then acknowledged — or redelivered when the lease expires, or dead-lettered after the maximum attempts.
Leases, redelivery, dead-letter
Only the lease holder can ack, nack or extend the lease, only while the lease is live — ack versus expiry is decided inside the SQL UPDATE. An expired lease redelivers; after the ceiling (defaults: high 3, normal 5, low 10) the message goes to dead-letter.
Delivery is at-least-once: dequeue order is priority, then accepted time, and per-recipient FIFO is best-effort.
get_pending claims 1–100 messages a call with a lease of 10–600 s and a long-poll of up to 20 s; claim and lease are one statement. extend_lease adds 10–600 s per call — the same tool over REST and MCP.
test:LeaseRaceTests
Scoped, once-disclosed credentials
One credential per AI account: 19 issuable scopes, shown once at issue and never again; operational credentials live at most 365 days, administrative at most 90; rotate with an overlap window; revoking an account revokes its credentials.
An MCP client that implements MCP authorization obtains one itself — it registers, a person approves once in a browser, and nothing is shown to copy; where it does not, or there is no browser, ask for the mcp_config form and the response carries paste-ready client config next to the once-disclosed plaintext.
Every send takes a mandatory idempotency key, retained 24 h; a replay returns the original response bytes, and a key reused with a different body is refused.
form:mcp_config
An audit chain you can verify
Every workspace has a per-workspace SHA-256 audit chain, hashed inside the insert transaction by a serialised stored procedure; weekly ECDSA P-256 seals of the chain head are built and not yet enabled, with the keys already at /.well-known/postmq-signing-keys.
Tamper-evident by construction, not immutable: the chain is re-verified daily, and a break freezes the workspace's write path and raises an integrity alert event. Browse your own chain in the dashboard and read it over REST and MCP, with the operator-significant families withheld. Public publication of the log is opt-in, and the weekly sealing behind it is built and not yet enabled, so no seal is being produced.
proc:audit_chain_serialized_insert
Every send commits atomically — envelope and payload, the idempotency record, the audit event and, when the
recipient has a webhook, the outbox row — in one transaction. Every payload is checked against a built-in
template's versioned JSON Schema — the review loop uses directive, assessment and
freeform_note, and there are no custom templates; validate is a first-class dry run. Recipients are AI
accounts in the same workspace; people see the messages in the dashboard. The leases, the ceilings, the
webhooks and every honest limit are on the handoffs page.
PostMQ keeps every hand-off the way email does: addressed, durable, and waiting in the inbox of the agent it is for. Wake adds what a chat app has: when a message is ready, the agent is woken to pick it up where its client allows, instead of the message waiting for its next check. It needs one outbound connection from the agent's machine, so it works from a laptop with no public address.
Measured on our one-core dev environment on 2026-10-26, from a laptop over the public internet: a listening
agent heard about a message 66 ms after a send through the REST API and 0.6 s after one through the MCP server
(medians), and 60 listening agents used about 4% of one core.
A readiness signal, never a delivery
GET /v1/messages/events is a Server-Sent Events stream of the caller's own inbox. When something there becomes leasable, it sends a wake event: how many messages are waiting, and the newest one's sender, template, subject, priority and correlation id.
A wake is a readiness signal: it carries no payload and takes no lease. The agent still calls get_pending and
ack, so leases, redelivery, idempotency and at-least-once delivery are unchanged, and a duplicate wake costs one
empty poll. If the listener stops, nothing is lost — the inbox is the durable record, and every new connection
starts by reporting all of it.
route:GET /v1/messages/events
Outbound, so a laptop can listen
The connection runs from the agent's machine to its own inbox, authenticated by the agent's own credential with the get_pending scope; there is no way to name another account's inbox.
A webhook cannot reach a laptop: the webhook destination allowlist refuses private and loopback addresses by
design, so webhooks stay the push path for hosted services. The stream re-checks its credential
every 30 seconds, so revoking the credential or freezing the account ends it within about a minute.
scope:get_pending
Built not to loop
Only something newly leasable wakes an agent. A lease or an ack can only drain the stream, so an agent's own ack never wakes it.
Two agents answering each other could still spend your tokens, so the receiver keeps a wake budget: on Claude
Code, three wakes in a row without a human turn; for a headless worker, twelve an hour. Past it,
the wake goes to you as a desktop notification and waits. The wake notice a Claude Code session receives, or that
is queued into a Codex thread, carries none of the other agent's own words, frames its message as information to weigh rather than
instructions, and sends anything destructive back to you first.
One standard-library Python file from the installer kit, doing what each client allows. Claude Code: a Stop hook
with asyncRewake wakes an idle session by itself, or the receiver drives a headless worker. Antigravity and
ZCode: the agent starts the receiver as a background task, and the client resumes the idle session when that
task finishes. Codex: the wake is queued in the session's conversation, and while that conversation is open
Codex starts a turn on it by itself, or runs it as soon as a busy agent's turn ends; you also get a desktop
notification, and a headless worker resumes the thread with codex exec resume. Desktop
notifications work on macOS and Linux.
file:postmq-wake.py
It is also far cheaper to wait this way. An agent holding a get_pending long-poll re-checks the
database about ten times a second for as long as it waits; the server behind the wake stream reads the
listening inboxes in batched statements per replica, about once a second — one statement for up to a hundred
listening agents. Because it reads the inbox on that cadence rather than only on send, a message that becomes
leasable on its own — a nack's back-off running out, an expired lease returning it — wakes the agent too. We
built it after a warning one of our own agents sent another sat unread for about two hours while a wrong
diagnosis spread. The events and their fields are in the REST guide;
setup, the receiver's commands and each client's limits are in
the installer kit.
Use case
One agent builds. Another reviews.
The hand-off is the point: a review request leaves as a typed message, the findings come back as one, and neither agent has to be running when the other is.
Have Codex review what Claude Code built — or have a second session of the same client review the first. The
builder sends a directive; the reviewer pulls it with get_pending, does the review,
sends the findings back as a typed, schema-validated message, and acks with an outcome summary the builder
can read; the builder's next session pulls the findings and acts on them. Every hand-off lands on the
workspace's audit chain, and nobody pastes prose between terminals. The walkthrough — payloads, leases,
what the humans see — is multi-agent code review;
the wiring is PostMQ in Claude Code and
PostMQ in Codex.
Session state
The record the agent itself keeps
The agent that picks up a hand-off starts from these: four ledgers, each scoped to a project inside your workspace, written by the agent over MCP and read by humans in the dashboard. Each card names the object it is made of.
Decisions cannot be edited or deleted — a database trigger rejects deletes and every update except source
links. Corrections are new entries that point at the entry they supersede; the original is never edited.
Describe the change — file globs, operations, code patterns, languages, project attributes — and get back only
the rules whose every populated trigger dimension matches. Each rule shows how often it has been retrieved,
and the dashboard replays the exact retrieval an agent runs.
Five token classes and turns per build session, rolled up per project, day, week or month. Token counts, not
dollars. Idempotent per transcript: a re-report overwrites and never double-counts.
tool:record_build_session_usage
The loop
Ask before you act. Record what you did.
Six steps, in the order an agent takes them — the hand-off is the third. This is the protocol this repository runs on itself, every session; each tool links to its row in the catalogue.
Start — or resume
The same project, computer, branch and actor gets its active session back; otherwise a fresh one starts. A unique index makes that true.
Before touching a file: pass the files, operations and languages of the change and get back only the rules whose every populated trigger dimension matches.
The agent does the work. Anything another agent must do — a review, a test run — leaves as a typed message and waits in that agent’s inbox. If that agent is listening, PostMQ wakes it where its client allows; either way it collects the message with get_pending.
Anything not done becomes an open item with a priority. Resolving it later needs a closing PR, commit or notes; dismissing needs a reason; both are final.
The session closes with a history entry the next one reads first, then reports what it consumed with record_build_session_usage — token counts, not dollars.
A build session opens with start_build_session
— resumed if this project, computer, branch and actor already have one. Before touching a file the agent calls
query_applicable_rules
with the files, operations and languages it is about to change and gets back only the rules that match. It
works. It writes append_decision_log
for each material decision and file_backlog_item
for each deferral into the backlog. It closes with end_build_session
and reports record_build_session_usage.
Anything that needs another agent — a review, a test run — leaves as a
send and waits in
that agent's inbox; if that agent is listening, PostMQ
wakes it where its client allows, and either way it collects the message with its next get_pending.
And the pain worth not repeating is filed as a lesson, which moves forward
only — observed, documented, enforced — and becomes the rule the next session is handed.
The full nine-step protocol, with the frames, is on the session-state page;
why an agent needs this at all — context loss between sessions, and the hand-rolled fixes that break — is
the context-loss guide; how we run the two
skills on this repository, step by step, is the start-session and end-session guide.
1,307of 1,348commits on main carrying a session trailer
As of 2026-10-26 — the project's own ledger and git history, read at build.
We run PostMQ on PostMQ. Every number here is generated from the project's own ledger at build — never typed —
and the dogfood check fails the build when the file goes stale. The session trailer on a commit is what ties it
to the session that produced it. The launch plan in the repository is generated from the live backlog and fails
closed on any open item without a tier. More than 4,000 automated tests; the integration suites run against
real SQL Server containers, not mocks.
SpecStep, Valuly and MeetCrew were built with the same session-state system PostMQ hosts.
Where it fits
Clients, protocols, the service, the dashboard
Claude Code, any MCP client, CI or the pmq CLI connect over MCP or REST; PostMQ holds the ledgers and the messages; the dashboard reads the same records.
PostMQ speaks standard MCP, so any MCP client can connect — Streamable HTTP at mcp.postmq.com, and
stdio when co-located. Claude Code,
Codex,
ZCode and Antigravity are the four AI coders we run
ourselves every day; the MCP clients page covers all four,
plus every other client that speaks MCP. A versioned REST API under
/v1 exposes everything the MCP server and the dashboard can do — all three run the same service
layer, and for every session-state aggregate REST and MCP are tested byte-identical. Messages travel in an open
envelope specification, Apache 2.0 — publication pending. The service is .NET, hosted in Microsoft Azure,
United States — a single region. It sits beside the memory your coding client already keeps rather than in
place of it: PostMQ and Claude Code
auto memory compares the two row by row, with a public source on both sides of each row.
Two readers
Governance by record. One approval to the first session.
For CTOs — governance by record, not by gate
PostMQ makes AI-coding work attributable, append-only and inspectable; it does not block a tool call. The
decision log and the backlog's terminal states are enforced by database triggers, and every session-state
lifecycle event — sessions started and ended, decisions appended and corrected, backlog filed and triaged,
lessons filed and enforced, rules created and changed — is written into the same tamper-evident SHA-256 chain as messaging: 38 of 160 event types. High-frequency touches such as pings and usage reports are deliberately not audited. Usage lands per
session, next to the decisions it paid for — token counts, not dollars.
Audit-ready by design; no SOC 2 yet — we say so, and no SLA in developer preview. The mechanisms, the test
names and what is not built are on the security page, with a
vendor questionnaire answered from the code. The
whole argument, including what PostMQ deliberately does not do, is on
the AI coding governance page. If you are
evaluating this for an organisation, the enterprise page
is the buyer's half — what you get, what you must accept, and what a reviewer can check today.
For developers — one approval, then the protocol
Create a workspace
Sign up at app.postmq.com. A workspace holds your AI accounts, projects and every ledger.
app.postmq.com/signup
Point your agent at the server
One line of client config and no credential. The server answers 401 with the metadata that tells a client implementing MCP authorization where to ask.
https://mcp.postmq.com/
Approve it once, then start a session
A client that implements MCP authorization registers itself and opens a browser. You see the scopes it asks for and approve or deny them as a set, and pick the AI account it acts as — created in that click if the workspace has none — after which it holds its own credential; otherwise supply one you issued. Then call start_build_session.
The full quickstart, the mcp_config response with the secret redacted, and the usage hook are on
the docs page; pricing — the protocol is unlimited on every tier — is on
the pricing page.
Start a session. Leave a record.
Create a workspace, connect your agent, call start_build_session.