Skip to main content

Crate mail4agent_api

Crate mail4agent_api 

Source
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 from HarnessMailAckV1.
AckRequest
Requests an acknowledgement be recorded for message_id, on behalf of whoever the presented credential authenticated as (same discipline as SendRequest: no reader field to fill in).
AckResponse
Answers an AckRequest. Carries the recorded Ack back so the caller has the exact reader identity and timestamp the mailbox stamped, mirroring how SendResponse hands 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.md keeps 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 calling Declared::into_inner or Declared::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. See SessionCard for where the split this type exists to preserve actually matters.
DeliveryNotification
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 subject or body.
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).
DirectoryEntry
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 (mirrors mail4agent_core::store::ParticipantSummary, the store-side type this is assembled from). sessions defaults to empty on decode so a payload written before this field existed still deserializes.
InboxPage
A page of a caller’s inbox.
InboxRequest
Requests a page of the caller’s inbox. limit defaults to INBOX_LIMIT_DEFAULT when a caller’s JSON omits it, and is bounded by INBOX_LIMIT_MAX.
Message
A stored message. refs defaults 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.
MessageGetRequest
Requests one message by id.
MessageId
Opaque, prefixed, fixed-width hex id for a stored Message. Ported from HarnessMailMessageId (prefix hmail_ there, m4a_ here so a value can never be mistaken for a harness message id from the crate this was ported out of).
MessageRef
Names a reference a message carries: a kind (a selector), a locator (an opaque pointer, meaningful only to the calling application), and an optional digest (a lower-hex content hash of whatever the locator names).
Participant
A registered participant. label is 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 (see SendRequest).
ParticipantId
Addresses one participant directly. Distinct from RoomId on 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.
RoomEntry
One room in the mailbox’s directory: its id, and whether the caller who asked for the directory currently belongs to it. member is relative to that one caller – two different callers reading the directory at the same moment see the same RoomEntry::id with whatever RoomEntry::member value is true for each of them.
RoomId
Addresses a named group of participants the mailbox itself tracks. Distinct from ParticipantId on purpose (see there).
SendRequest
Requests a send. Has no from field and must never grow one. The sender is whoever the presented credential authenticated as; the mailbox derives from from the verified identity, never from a field the caller filled in.
SendResponse
Answers a SendRequest. Returns the caller its own address so a participant that just wrote immediately knows where it can be answered.
SessionAttested
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 (see Declared), so this is the wire shape of the same three facts.
SessionCard
What the mailbox knows about one session, split by how sure it can be: SessionAttested from the kernel, SessionCorroborated from the process’s own command line, SessionDeclared said 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. See mailbox-service-extraction-and-signed-session-identity-2026-09-16.md §5e.
SessionCorroborated
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 Declared for that reason; see its doc comment before treating any of these with the same weight as SessionAttested.
SessionDeclared
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.
SessionEntry
One session under an account, as the mailbox’s directory reports it. live is filled by the mailbox from a liveness check it is given, not one it performs itself – mail4agent-core learns nothing about processes or Windows; see MailboxEngine::directory.
SessionId
Opaque id for one live session under a ParticipantId account. Derived by the caller from a process identity and handed to this crate already formed – MailboxEngine::ensure_session registers one, it never invents one.
UnreadCount
Answers an UnreadCountRequest.
UnreadCountRequest
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").
MailError
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; see mail4agent/CLAUDE.md).

Constants§

BODY_MAX_BYTES
Maximum size of Message::body / SendRequest::body, in bytes (not characters).
INBOX_LIMIT_DEFAULT
Value InboxRequest::limit defaults 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 – unlike INBOX_LIMIT_MAX, which InboxRequest::validate refuses 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 way mirage2operator/crates/operator-box/src/ops/mcp.rs’s own GET /ops/jobs/{id}?wait_secs=N caps 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’s opaque_id! ids.
MESSAGE_ID_PREFIX
Prefix every MessageId carries. Mirrors HarnessMailMessageId’s own hmail_ 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 from HARNESS_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. Unlike MESSAGE_ID_HEX_LEN this 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 from HARNESS_SELECTOR_MAX_BYTES / validate_selector.
SESSION_ID_HEX_MAX_CHARS
Maximum length, in lower-hex characters, of a SessionId’s body. Unlike MessageId, 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 (see SESSION_ID_HEX_MAX_CHARS for why there is no exact width).
SESSION_ID_PREFIX
Prefix every SessionId carries, chosen so a printed address like claude/s-7f3a... reads unambiguously as “an account, then one of its sessions” – see Address’s Display/FromStr.
SUBJECT_MAX_BYTES
Maximum size of Message::subject / SendRequest::subject, in bytes (not characters).