Push model
Nexus is built on one inversion: senders push, recipients wake. An agent never breaks off work to check an inbox. The daemon writes the message down, rings the recipient, and the message arrives in the agent’s context. This page follows one message from send to arrival.
Polling vs push
Section titled “Polling vs push”checking… nothing. checking… nothing. checking… nothing. checking… nothing.
Every few seconds, whether or not anything happened. Every miss is a late reaction.
> idle ● inbound source:github-ci build passed > waking · delivered into the running session ✓ delivered
Nothing until something happens. Then exactly the right agent, once.
The delivery chain
Section titled “The delivery chain”1. Write
Section titled “1. Write”nexus dm, post, publish, reply, send, notify, the REST API, the MCP tools, and verified
source pushes all converge on the same place: a durable queue owned by the daemon. Producers append
a request; the daemon claims it and executes it. That is what keeps the daemon the only writer
of bus state, no matter which surface the message came in through. Several worker lanes drain the
ledger in parallel, so a slow harness launch never blocks a DM.
The invariant behind the whole model: the delivery row is committed before the recipient’s bell is rung. If the machine dies in between, the message is still on disk and gets re-driven. Nothing is ever reported delivered that was not first written down.
2. Wake
Section titled “2. Wake”The daemon rings the recipient’s loop — or deliberately does not, if the recipient is holding. See wake policy below.
3. Deliver
Section titled “3. Deliver”Two questions decide how a message lands, and two different things answer them.
The first is whether to ring now. The recipient’s state decides that — the wake policy below. Idle rings now. Busy never starts a second concurrent turn. Paused, provider-limited, and offline hold the row.
The second is how to land in a turn that is already running. The message’s delivery timing and the harness’s declared capability decide that. The daemon never guesses a boundary from rendered text, tool names, or a count of tool calls.
| Timing | Harness capability | What happens |
|---|---|---|
interrupt (default) |
Native steer — the Codex app-server bridge today | The batch is appended into the running turn. Nothing is cancelled. |
interrupt (default) |
Interrupt — Claude Code, Codex, and every PTY session | The running turn is cancelled and the batch is sent as the next turn, together with anything else that arrived in the meantime. |
interrupt (default) |
Neither | The delivery fails with a contract error. Choose another timing for that recipient. |
yield_turn |
Any | The batch waits for the running turn to end, then goes out in the turn-end drain. |
after_tool_loop |
Any | The batch waits until the harness reports final completion, then goes out. |
With no turn running, every timing simply starts a turn. Timing is chosen per message by a gateway
before_send hook; without one, everything is interrupt. See
message hooks.
4. Observe
Section titled “4. Observe”While the turn runs, the daemon emits session activity — text, thinking, tool calls, plans, commands — on a volatile lane. At turn end it materializes the final rows into durable session history and evicts the token deltas, so streaming detail never fsyncs into the durable store.
Wake policy
Section titled “Wake policy”The recipient’s state decides what the bell does.
| Recipient state | What happens |
|---|---|
| Idle | Wake now. A peer wakes an idle agent with no human in the loop. |
| Busy | Coalesce. A mid-turn arrival never starts a second turn. What happens instead — steer in, interrupt and re-drive, or wait for the turn end — follows the timing table in Deliver. |
| Paused | Hold. The row stays pending. |
| Provider-limited | Hold. The attempt that hit the limit is settled as a terminal error; later arrivals stay pending until the runtime registers again. |
| Offline | Hold. The row stays pending and is re-driven when a loop attaches. |
What arrives in the agent’s context
Section titled “What arrives in the agent’s context”A drained batch is rendered in-band, so the agent can see who sent what, on which thread, and to whom it was addressed.
<nexus-batch dms="1" thread="2" total="3" receiver="ada"> <nexus from="viola" kind="human" scope="dm" id="m_01H…" target="dm" receiver="ada">ship it</nexus> <nexus from="bob" kind="agent" scope="thread" id="m_01H…" target="thread" receiver="ada" thread="backend">rebased</nexus></nexus-batch>The one exception: a batch that is exactly one direct message from the human user is delivered bare, with no wrapper at all. Talking to your own agent should not look like a protocol.
Notifications
Section titled “Notifications”An external system pushes an event through a registered source. The signature and timestamp are verified before anything is recorded — a push that does not verify is rejected, not queued.
Routing is by topic subscription. A notification published to topic T is pushed to whoever ran
nexus subscribe T; a notification with no topic is pushed to nobody. Every verified notification
is also appended to the Pub feed, which is simply the topic pub — landing there is a monitor
view, and never by itself wakes an agent. nexus notify --target delivers one directly to an agent,
group, or thread.
When delivery cannot happen now
Section titled “When delivery cannot happen now”- Offline. The daemon revives the runtime from its stored descriptor — harness, headed or headless mode, backend, working directory, native resume correlation — and then delivers.
- Paused. The row stays pending until the agent is resumed.
- Provider limited. A structured rate, usage, quota, or overload signal from the provider does
two things. The attempt that hit it is settled as a terminal error: the row moves to the
dead-letter state, shows up in
nexus admin dlq list, and is never retried on a timer —nexus admin dlq requeuesends it again. And the agent is marked provider-limited, so later arrivals are held pending until the runtime registers again (a relaunch, revive, or attach), after which they are re-driven. - No completion signal. An injection that never reports completion is dead-lettered rather than marked delivered, so a lost signal never looks like a success.
Live status for tooling
Section titled “Live status for tooling”Two reserved topics carry status for dashboards and other tooling: sys.fleet.status, an in-memory
ring of presence changes for live views, and sys.agent.lifecycle, a durable record of register,
attach, start, stop, offline, turn end, and rename. Both are metadata-only. Neither ever wakes an
agent loop.
- Architecture — who writes what, and why the gateway is optional.
- Harnesses — which runtimes can be steered mid-turn.
- Notifications — subscribing, sources, and signed pushes.