--- meta-description: spt-core: a harness-independent core for an agent ecosystem — messaging, live-agent lifecycle, terminal hosting, P2P networking, and the runtime-manifest harness contract. meta-theme-color: #ffffff meta-viewport: width=device-width, initial-scale=1 title: Messaging - SPT developer docs --- ## Keyboard shortcuts Press `←` or `→` to navigate between chapters Press `S` or `/` to search in the book Press `?` to show this help Press `Esc` to hide this help ![SVG Image](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDggNTEyIj48cGF0aCBkPSJNMCA5NkMwIDc4LjMgMTQuMyA2NCAzMiA2NEg0MTZjMTcuNyAwIDMyIDE0LjMgMzIgMzJzLTE0LjMgMzItMzIgMzJIMzJDMTQuMyAxMjggMCAxMTMuNyAwIDk2ek0wIDI1NmMwLTE3LjcgMTQuMy0zMiAzMi0zMkg0MTZjMTcuNyAwIDMyIDE0LjMgMzIgMzJzLTE0LjMgMzItMzIgMzJIMzJjLTE3LjcgMC0zMi0xNC4zLTMyLTMyek00NDggNDE2YzAgMTcuNy0xNC4zIDMyLTMyIDMySDMyYy0xNy43IDAtMzItMTQuMy0zMi0zMnMxNC4zLTMyIDMyLTMySDQxNmMxNy43IDAgMzIgMTQuMyAzMiAzMnoiIC8+PC9zdmc+) ![SVG Image](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIj48cGF0aCBkPSJNMzcxLjMgMzY3LjFjMjcuMy0zLjkgNTEuOS0xOS40IDY3LjItNDIuOUw2MDAuMiA3NC4xYzEyLjYtMTkuNSA5LjQtNDUuMy03LjYtNjEuMlM1NDkuNy00LjQgNTMxLjEgOS42TDI5NC40IDE4Ny4yYy0yNCAxOC0zOC4yIDQ2LjEtMzguNCA3Ni4xTDM3MS4zIDM2Ny4xem0tMTkuNiAyNS40bC0xMTYtMTA0LjRDMTc1LjkgMjkwLjMgMTI4IDMzOS42IDEyOCA0MDBjMCAzLjkgLjIgNy44IC42IDExLjZjMS44IDE3LjUtMTAuMiAzNi40LTI3LjggMzYuNEg5NmMtMTcuNyAwLTMyIDE0LjMtMzIgMzJzMTQuMyAzMiAzMiAzMkgyNDBjNjEuOSAwIDExMi01MC4xIDExMi0xMTJjMC0yLjUtLjEtNS0uMi03LjV6IiAvPjwvc3ZnPg==) - Auto - Light - Rust - Coal - Navy - Ayu ![SVG Image](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBkPSJNNDE2IDIwOGMwIDQ1LjktMTQuOSA4OC4zLTQwIDEyMi43TDUwMi42IDQ1Ny40YzEyLjUgMTIuNSAxMi41IDMyLjggMCA0NS4zcy0zMi44IDEyLjUtNDUuMyAwTDMzMC43IDM3NmMtMzQuNCAyNS4yLTc2LjggNDAtMTIyLjcgNDBDOTMuMSA0MTYgMCAzMjIuOSAwIDIwOFM5My4xIDAgMjA4IDBTNDE2IDkzLjEgNDE2IDIwOHpNMjA4IDM1MmM3OS41IDAgMTQ0LTY0LjUgMTQ0LTE0NHMtNjQuNS0xNDQtMTQ0LTE0NFM2NCAxMjguNSA2NCAyMDhzNjQuNSAxNDQgMTQ0IDE0NHoiIC8+PC9zdmc+) # SPT developer docs [![SVG Image](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBkPSJNMTI4IDBDOTIuNyAwIDY0IDI4LjcgNjQgNjR2OTZoNjRWNjRIMzU0LjdMMzg0IDkzLjNWMTYwaDY0VjkzLjNjMC0xNy02LjctMzMuMy0xOC43LTQ1LjNMNDAwIDE4LjdDMzg4IDYuNyAzNzEuNyAwIDM1NC43IDBIMTI4ek0zODQgMzUydjMyIDY0SDEyOFYzODQgMzY4IDM1MkgzODR6bTY0IDMyaDMyYzE3LjcgMCAzMi0xNC4zIDMyLTMyVjI1NmMwLTM1LjMtMjguNy02NC02NC02NEg2NGMtMzUuMyAwLTY0IDI4LjctNjQgNjR2OTZjMCAxNy43IDE0LjMgMzIgMzIgMzJINjR2NjRjMCAzNS4zIDI4LjcgNjQgNjQgNjRIMzg0YzM1LjMgMCA2NC0yOC43IDY0LTY0VjM4NHptLTE2LTg4Yy0xMy4zIDAtMjQtMTAuNy0yNC0yNHMxMC43LTI0IDI0LTI0czI0IDEwLjcgMjQgMjRzLTEwLjcgMjQtMjQgMjR6IiAvPjwvc3ZnPg==)](../print.html "Print this book") [![SVG Image](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OTYgNTEyIj48cGF0aCBkPSJNMTY1LjkgMzk3LjRjMCAyLTIuMyAzLjYtNS4yIDMuNi0zLjMuMy01LjYtMS4zLTUuNi0zLjYgMC0yIDIuMy0zLjYgNS4yLTMuNiAzLS4zIDUuNiAxLjMgNS42IDMuNnptLTMxLjEtNC41Yy0uNyAyIDEuMyA0LjMgNC4zIDQuOSAyLjYgMSA1LjYgMCA2LjItMnMtMS4zLTQuMy00LjMtNS4yYy0yLjYtLjctNS41LjMtNi4yIDIuM3ptNDQuMi0xLjdjLTIuOS43LTQuOSAyLjYtNC42IDQuOS4zIDIgMi45IDMuMyA1LjkgMi42IDIuOS0uNyA0LjktMi42IDQuNi00LjYtLjMtMS45LTMtMy4yLTUuOS0yLjl6TTI0NC44IDhDMTA2LjEgOCAwIDExMy4zIDAgMjUyYzAgMTEwLjkgNjkuOCAyMDUuOCAxNjkuNSAyMzkuMiAxMi44IDIuMyAxNy4zLTUuNiAxNy4zLTEyLjEgMC02LjItLjMtNDAuNC0uMy02MS40IDAgMC03MCAxNS04NC43LTI5LjggMCAwLTExLjQtMjkuMS0yNy44LTM2LjYgMCAwLTIyLjktMTUuNyAxLjYtMTUuNCAwIDAgMjQuOSAyIDM4LjYgMjUuOCAyMS45IDM4LjYgNTguNiAyNy41IDcyLjkgMjAuOSAyLjMtMTYgOC44LTI3LjEgMTYtMzMuNy01NS45LTYuMi0xMTIuMy0xNC4zLTExMi4zLTExMC41IDAtMjcuNSA3LjYtNDEuMyAyMy42LTU4LjktMi42LTYuNS0xMS4xLTMzLjMgMi42LTY3LjkgMjAuOS02LjUgNjkgMjcgNjkgMjcgMjAtNS42IDQxLjUtOC41IDYyLjgtOC41czQyLjggMi45IDYyLjggOC41YzAgMCA0OC4xLTMzLjYgNjktMjcgMTMuNyAzNC43IDUuMiA2MS40IDIuNiA2Ny45IDE2IDE3LjcgMjUuOCAzMS41IDI1LjggNTguOSAwIDk2LjUtNTguOSAxMDQuMi0xMTQuOCAxMTAuNSA5LjIgNy45IDE3IDIyLjkgMTcgNDYuNCAwIDMzLjctLjMgNzUuNC0uMyA4My42IDAgNi41IDQuNiAxNC40IDE3LjMgMTIuMUM0MjguMiA0NTcuOCA0OTYgMzYyLjkgNDk2IDI1MiA0OTYgMTEzLjMgMzgzLjUgOCAyNDQuOCA4ek05Ny4yIDM1Mi45Yy0xLjMgMS0xIDMuMy43IDUuMiAxLjYgMS42IDMuOSAyLjMgNS4yIDEgMS4zLTEgMS0zLjMtLjctNS4yLTEuNi0xLjYtMy45LTIuMy01LjItMXptLTEwLjgtOC4xYy0uNyAxLjMuMyAyLjkgMi4zIDMuOSAxLjYgMSAzLjYuNyA0LjMtLjcuNy0xLjMtLjMtMi45LTIuMy0zLjktMi0uNi0zLjYtLjMtNC4zLjd6bTMyLjQgMzUuNmMtMS42IDEuMy0xIDQuMyAxLjMgNi4yIDIuMyAyLjMgNS4yIDIuNiA2LjUgMSAxLjMtMS4zLjctNC4zLTEuMy02LjItMi4yLTIuMy01LjItMi42LTYuNS0xem0tMTEuNC0xNC43Yy0xLjYgMS0xLjYgMy42IDAgNS45IDEuNiAyLjMgNC4zIDMuMyA1LjYgMi4zIDEuNi0xLjMgMS42LTMuOSAwLTYuMi0xLjQtMi4zLTQtMy4zLTUuNi0yeiIgLz48L3N2Zz4=)](https://github.com/SaberMage/spt-releases "Git repository") ![SVG Image](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBkPSJNMzA0IDQ4YzAtMjYuNS0yMS41LTQ4LTQ4LTQ4cy00OCAyMS41LTQ4IDQ4czIxLjUgNDggNDggNDhzNDgtMjEuNSA0OC00OHptMCA0MTZjMC0yNi41LTIxLjUtNDgtNDgtNDhzLTQ4IDIxLjUtNDggNDhzMjEuNSA0OCA0OCA0OHM0OC0yMS41IDQ4LTQ4ek00OCAzMDRjMjYuNSAwIDQ4LTIxLjUgNDgtNDhzLTIxLjUtNDgtNDgtNDhzLTQ4IDIxLjUtNDggNDhzMjEuNSA0OCA0OCA0OHptNDY0LTQ4YzAtMjYuNS0yMS41LTQ4LTQ4LTQ4cy00OCAyMS41LTQ4IDQ4czIxLjUgNDggNDggNDhzNDgtMjEuNSA0OC00OHpNMTQyLjkgNDM3YzE4LjctMTguNyAxOC43LTQ5LjEgMC02Ny45cy00OS4xLTE4LjctNjcuOSAwcy0xOC43IDQ5LjEgMCA2Ny45czQ5LjEgMTguNyA2Ny45IDB6bTAtMjk0LjJjMTguNy0xOC43IDE4LjctNDkuMSAwLTY3LjlTOTMuNyA1Ni4yIDc1IDc1cy0xOC43IDQ5LjEgMCA2Ny45czQ5LjEgMTguNyA2Ny45IDB6TTM2OS4xIDQzN2MxOC43IDE4LjcgNDkuMSAxOC43IDY3LjkgMHMxOC43LTQ5LjEgMC02Ny45cy00OS4xLTE4LjctNjcuOSAwcy0xOC43IDQ5LjEgMCA2Ny45eiIgLz48L3N2Zz4=) # [Messaging](#messaging) The substrate everything else rides on: durable, addressed, reply-routable messages between endpoints — live when the target listens, spooled when it doesn’t, across machines once nodes are paired. You’ve probably already run the [quickstart](../quickstart/messaging.html); this page is the model. ## [Semantics](#semantics) - **Live-first, spool-fallback.** `spt send ` connects directly to a listening target (`SENT`); if the perch exists but nothing is listening, the message lands in the target’s durable spool (`QUEUED`) and drains on its next `ready`. A target with *no* perch is an error (`NO_PERCH`) — identity is never invented on someone else’s behalf. - **Reply routing.** Every message carries its sender id structurally; the arriving `` envelope surfaces it, and `spt send ` answers without knowing anything else. - **The blocking ask.** `spt ring ` sends and waits for the reply (with a timeout) — the synchronous question between agents. - **Per-message send control (three orthogonal axes).** Each `spt send` carries one value per axis; every axis defaults to unrestricted: - **Delivery window** (*when*) — `--active-only` spools for the agent’s own poll without waking a live listener (it reaches the agent at its next natural boundary instead of interrupting now; also held for resting dormant/suspended instances and released exactly once on wake). This **renames the older `--deferred`**, which still parses as a hidden alias. `--idle-only` holds until the target is idle, then delivers (the wake). Default delivers in whichever window fires first. - **Channel** (*through what*) — `--prefer-native` routes through the target’s translation binary when one is running, else falls back to the standard delivery; `--force-native` uses the binary only (no fallback, no reroute — if no binary is live it reports non-delivery rather than spooling to another method). Default is unrestricted. The translation binary is the adapter’s idle-delivery filter; an adapter declares it with a `[message-idle-translation-binary].command` (a program token plus args, new in v0.16.0 — the bare `path` form is deprecated) and spt-core lifecycle-manages it. - **Persistence** (*how long*) — `--ephemeral` drops the message if it can’t be delivered in its accepted window instead of spooling; it is the one path allowed to drop silently (everything else spools and reports non-delivery). *In this release ephemeral evaporation applies to translation-binary delivery and TTL expiry; the harness-relay carrier-absence case is not yet wired.* - **Opaque metadata.** `--json-payload ''` attaches a JSON metadata block alongside the body. spt-core carries it verbatim across every rail and never interprets it — the **receiving adapter** parses it. It can’t forge spt-core’s own envelope attributes (it rides inside a single `json` value), and any sender may attach it. - **Typed payloads.** Message bodies carry typed operations and file blobs, not just text — file transfers are addressable and progress-queryable mid-flight. ## [Send outcomes — the closed set](#send-outcomes--the-closed-set) Every `spt send` reports exactly one outcome line. The line goes to **stderr** (stdout is reserved for message payloads); classify success by the **exit code** — `0` for every delivered/spooled outcome, non-zero for every failure. The token before the first `:` is stable; this set is complete as of v0.26.0. **Success (exit 0):** | Line | Meaning | | --- | --- | | `SENT:` | Delivered live to a listening target on this node. | | `SENT(WAN):@` | Delivered cross-node, **receiver-confirmed** — printed only when the remote daemon acknowledged. A suffix annotates the confirmed disposition: ` (spooled)` — the remote daemon accepted it into the target’s durable spool; ` (duplicate)` — the receiver had already processed this message (a safe dedup’d replay). No suffix = delivered live. | | `QUEUED:` | The perch exists but nothing is listening — spooled durably, drains on the target’s next `ready`. Success, not an error. | | `QUEUED(idle-only):` | An `--idle-only` send holding for the target’s idle window. | | `DEFERRED:` | An `--active-only` send spooled for the target’s own next poll (never interrupts a live listener). | **Failure (non-zero exit, stderr):** | Line | Meaning | | --- | --- | | `NO_PERCH: is not listening` | No perch for that id — identity is never invented on someone else’s behalf. An `--active-only` send reports this with `(active-only stays local-only)`: the hook channel does not take the cross-node leg. | | `WAN_NO_PERCH: — no perch on ` | The route resolved to a node, but no perch lives there (a stale route — the endpoint may have moved or stopped). | | `WAN_REFUSED:@` | The receiver denied the message (access gate). | | `WAN_UNCONFIRMED:@` | No receiver acknowledgment — the peer may be offline or on an old version. The message may or may not have landed; only `SENT(WAN)` means confirmed. | | `WAN_FAIL: — ` | Cross-node transport failure. | | `AMBIGUOUS: — ` | Several nodes host that id — qualify (`@`). | | `EMPTY_MSG` | Refused: empty body. | ## [The `` wire contract](#the-event-wire-contract) Every arriving message on an **agent** surface (`spt ready`, `api listen`, `api poll`, `api worker-poll`) is one `body` envelope — never a bare body: hello check the build A body that is already a fully-formed typed envelope (`echo_commune`, `notify`, `user-msg`, …) passes through **verbatim** — one envelope, never re-wrapped, and the body’s own `from` wins. On the listener stream an oversized line splits into `` chunks the receiver reassembles; `api poll` / `api worker-poll` always emit one whole envelope per message, never chunked. **Escaping — the closed entity set.** Exactly four entities, plus the newline token; there is no `'`/`'` (single quotes ride literal): - **Encode (body):** `&` → `&` **first**, then `<` → `<`, `>` → `>`, `"` → `"`; then CRLF and lone CR normalize to LF, and LF → `
`. - **Decode (body):** split/replace `
` → newline **first**, then `<` → `<`, `>` → `>`, `"` → `"`, and `&` → `&` **last**. Amp-last is the invariant that keeps an embedded `&lt;` from double-decoding into `<`. - **Attribute values:** the same four entities in the same order, with no `
` step (attribute values are line-safe by construction). - Decode only the **extracted body substring** after parsing the envelope framing — never run the entity decode over the full line, or the framing tokens themselves unescape. **MAC-stamped frames are a different surface.** The shell relay drain (`api poll --link `) emits raw stamped frames of the form ` ` — a 64-hex-char HMAC-SHA256 over the frame bytes, one space, then the frame — and is deliberately **not** ``-wrapped (the shell child verifies the MAC and parses its own vocabulary). Agent-perch surfaces never emit stamped frames; a parser of agent traffic only ever sees `` / `` lines. ## [Addressing](#addressing) Bare ids (`sergey`) resolve locally first, then across the subnet; when the same id is live on several nodes, resolution **refuses and asks you to qualify** (`sergey@desktop` — node labels and key prefixes both work) rather than guessing. The full form is `[subnet:]id[@node]`. ## [Commands](#commands) `send` · `ring` · `ready` (blocks; `--once` drains and exits) · `list` · `stop` · `whoami` — every flag in the [CLI reference](../cli/reference.html). Agents get the task-oriented version from the binary itself: `spt how-to ready` / `spt how-to send`.