Expand description
Wire types for the mail4agent mailbox: addresses, messages, requests and named refusals.
This crate is the wire contract only – serde and nothing else. It knows
about participants, addresses and messages; it knows nothing about tasks,
schedulers, runs, grants, workspaces, nodes, or any particular system that
happens to run agents (see mail4agent/CLAUDE.md).
Ported from gate4agent-harness-protocol’s mail surface
(docs/gate4agent/research/mailbox-code-inventory-2026-09-16.md section
1.1): the discipline is kept – bounded, validated on both encode and
decode, versioned by convention, legacy-tolerant on read – while every
name that leaked a harness/task-kernel concept (HarnessRecordRef,
HarnessMailAddressV1::Session/Task, task_id) is replaced by a
general shape a standalone mailbox can own outright.
The rule that defines this service: a sender is never a field the
caller fills in. See SendRequest.
Structs§
- Ack
- Per-reader acknowledgement. Dedup key is
(message_id, reader), never the message alone – a room-addressed message has one ack per reader, not one total. Ported fromHarnessMailAckV1. - AckRequest
- Requests an acknowledgement be recorded for
message_id, on behalf of whoever the presented credential authenticated as (same discipline asSendRequest: no reader field to fill in). - AckResponse
- Answers an
AckRequest. Carries the recordedAckback so the caller has the exact reader identity and timestamp the mailbox stamped, mirroring howSendResponsehands the caller its own address. - Declared
- A value corroborated from a source that is real but not proof – read
out of a process’s own command line, for instance, rather than attested
by the kernel. Mirrors
mail4agent-attest::Declared, which this crate cannot depend on directly:mail4agent/CLAUDE.mdkeeps this crate’s dependency list empty of everything that is not serialisation, and that crate links Windows process APIs to do its job. Getting the inner value means callingDeclared::into_innerorDeclared::inner_ref, never a plain field read, so a caller cannot treat a corroborated fact with the same weight as an attested one by accident. SeeSessionCardfor where the split this type exists to preserve actually matters. - Delivery
Notification - The body a registered delivery listener receives at its own URL when
mail arrives for the account that registered it, or for any of that
account’s sessions. Carries only ids, never
subjectorbody. - Directory
- Answers a directory request: every participant the mailbox has
registered and every room it tracks, from the point of view of
whoever asked (see
RoomEntry::member). The whole mailbox’s population in one call, deliberately unpaginated – this is a small, local directory, not a social graph (mail4agent/CLAUDE.md). - Directory
Entry - One account in the mailbox’s directory, with its live sessions
nested under it – the XMPP/Matrix shape (an account, then its
individually addressable sessions), not a flat list of CLI brands. See
mailbox-service-extraction-and-signed-session-identity-2026-09-16.md§5e. Never carries a secret digest or a permission bit – a directory answers “who exists”, not “what may they do” or anything that would help forge them, and a type that structurally has no such field cannot leak one even by accident (mirrorsmail4agent_core::store::ParticipantSummary, the store-side type this is assembled from).sessionsdefaults to empty on decode so a payload written before this field existed still deserializes. - Inbox
Page - A page of a caller’s inbox.
- Inbox
Request - Requests a page of the caller’s inbox.
limitdefaults toINBOX_LIMIT_DEFAULTwhen a caller’s JSON omits it, and is bounded byINBOX_LIMIT_MAX. - Message
- A stored message.
refsdefaults on decode (harness_mail_message_old_shape_without_refs_deserializes’s own forward-compatibility property, kept here) so a message written before a future field addition still deserializes with an empty ref list rather than failing decode. - Message
GetRequest - Requests one message by id.
- Message
Id - Opaque, prefixed, fixed-width hex id for a stored
Message. Ported fromHarnessMailMessageId(prefixhmail_there,m4a_here so a value can never be mistaken for a harness message id from the crate this was ported out of). - Message
Ref - Names a reference a message carries: a
kind(a selector), alocator(an opaque pointer, meaningful only to the calling application), and an optionaldigest(a lower-hex content hash of whatever the locator names). - Participant
- A registered participant.
labelis display metadata set when the participant registers; it is never accepted on a send – a sender’s identity comes from its credential, not from a caller-supplied field (seeSendRequest). - Participant
Id - Addresses one participant directly. Distinct from
RoomIdon purpose: a room id must never be accepted where a participant id is meant, and a shared alias would let one slip into the other’s slot. - Room
Entry - One room in the mailbox’s directory: its id, and whether the caller
who asked for the directory currently belongs to it.
memberis relative to that one caller – two different callers reading the directory at the same moment see the sameRoomEntry::idwith whateverRoomEntry::membervalue is true for each of them. - RoomId
- Addresses a named group of participants the mailbox itself tracks. Distinct from
ParticipantIdon purpose (see there). - Send
Request - Requests a send. Has no
fromfield and must never grow one. The sender is whoever the presented credential authenticated as; the mailbox derivesfromfrom the verified identity, never from a field the caller filled in. - Send
Response - Answers a
SendRequest. Returns the caller its own address so a participant that just wrote immediately knows where it can be answered. - Session
Attested - Proved by the kernel at the moment the connection carrying this
session’s request was accepted – never rewritable by the process it
describes. Mirrors the three kernel-sourced fields of
mail4agent-attest::PeerProcess(pid,started_at_unix_ms,exe); this crate cannot depend on that one directly (seeDeclared), so this is the wire shape of the same three facts. - Session
Card - What the mailbox knows about one session, split by how sure it can be:
SessionAttestedfrom the kernel,SessionCorroboratedfrom the process’s own command line,SessionDeclaredsaid by the session about itself. Kept as three distinct nested structs – never flattened into one – so the provenance of every field is visible in the type and survives into the JSON exactly as it should be trusted. Seemailbox-service-extraction-and-signed-session-identity-2026-09-16.md§5e. - Session
Corroborated - Read out of the process’s own command line (or a CLI hook) – real in
the sense that some process held this in memory at read time, and
never proof of what that process actually is or was launched with. Each
field is
Declaredfor that reason; see its doc comment before treating any of these with the same weight asSessionAttested. - Session
Declared - Said by the session about itself – the weakest tier, and the only one a
session can write at all: see
MailboxEngine::set_declared, the sole way this group is ever set. - Session
Entry - One session under an account, as the mailbox’s directory reports it.
liveis filled by the mailbox from a liveness check it is given, not one it performs itself –mail4agent-corelearns nothing about processes or Windows; seeMailboxEngine::directory. - Session
Id - Opaque id for one live session under a
ParticipantIdaccount. Derived by the caller from a process identity and handed to this crate already formed –MailboxEngine::ensure_sessionregisters one, it never invents one. - Unread
Count - Answers an
UnreadCountRequest. - Unread
Count Request - Requests the unread count for
target– an account or one of its sessions.
Enums§
- Address
- Where a message goes, or who it is from: one account directly, one of
that account’s live sessions, or a room the mailbox tracks membership
for. Serde-tagged on
kind("direct"/"session"/"room"). - Mail
Error - A named refusal. Every variant names its inputs so a caller learns what
was refused and why from the refusal alone – never a bare
Internal(the crate contract’s own discipline; seemail4agent/CLAUDE.md).
Constants§
- BODY_
MAX_ BYTES - Maximum size of
Message::body/SendRequest::body, in bytes (not characters). - INBOX_
LIMIT_ DEFAULT - Value
InboxRequest::limitdefaults to when a caller’s JSON omits it. - INBOX_
LIMIT_ MAX - Maximum value accepted for
InboxRequest::limit. - INBOX_
WAIT_ SECS_ MAX - Ceiling on
InboxRequest::wait_secs. Clamped, not refused, when a caller asks for longer – unlikeINBOX_LIMIT_MAX, whichInboxRequest::validaterefuses outright above. The daemon holds an HTTP (or MCP) connection open for the whole wait, so this door must answer within a bounded time the same waymirage2operator/crates/operator-box/src/ops/mcp.rs’s ownGET /ops/jobs/{id}?wait_secs=Ncaps its one long-poll shape, for the same reason: nothing about this door streams, so nothing about it may hold a connection open indefinitely either. - MESSAGE_
ID_ HEX_ LEN - Length, in lower-hex characters, of a
MessageId’s body after its prefix. Same width as the source’sopaque_id!ids. - MESSAGE_
ID_ PREFIX - Prefix every
MessageIdcarries. MirrorsHarnessMailMessageId’s ownhmail_prefix, renamed so a value can never be mistaken for a harness message id from the crate this was ported out of. - REFS_
MAX - Maximum number of entries in
Message::refs/SendRequest::refs. Ported verbatim fromHARNESS_MAIL_REFS_MAX: a message names a handful of dereferenceable results, never a manifest. - REF_
DIGEST_ MAX_ CHARS - Maximum length of
MessageRef::digest, in lower-hex characters, when present. UnlikeMESSAGE_ID_HEX_LENthis is a ceiling, not an exact width: the mailbox never interprets a digest, so it does not know (and must not assume) which hash algorithm produced it. - REF_
LOCATOR_ MAX_ BYTES - Maximum size of
MessageRef::locator, in bytes. - SELECTOR_
MAX_ BYTES - Bound shared by every selector-shaped id (
ParticipantId,RoomId,MessageRef::kind): ASCII, 1..=128 bytes. Ported fromHARNESS_SELECTOR_MAX_BYTES/validate_selector. - SESSION_
ID_ HEX_ MAX_ CHARS - Maximum length, in lower-hex characters, of a
SessionId’s body. UnlikeMessageId, a session id is derived by the caller from a process identity (mail4agent-attest::PeerProcess’s(pid, started_at_unix_ms)pair, typically hashed) and handed to this crate already formed, so it has no width this crate gets to fix – this bound is generous enough for a SHA-256 hex digest (64 characters), a reasonable way to derive one. - SESSION_
ID_ HEX_ MIN_ CHARS - Minimum length, in lower-hex characters, of a
SessionId’s body after its prefix – a floor against an accidentally-empty id, not an exact width (seeSESSION_ID_HEX_MAX_CHARSfor why there is no exact width). - SESSION_
ID_ PREFIX - Prefix every
SessionIdcarries, chosen so a printed address likeclaude/s-7f3a...reads unambiguously as “an account, then one of its sessions” – seeAddress’sDisplay/FromStr. - SUBJECT_
MAX_ BYTES - Maximum size of
Message::subject/SendRequest::subject, in bytes (not characters).