Behavior
IMAP operations are policy-gated before Arbiter issues upstream IMAP commands. The caller selects a configured account, folder, and message id; Arbiter supplies the connection settings, credentials, folder metadata, and access decisions from deployment config.
Command Input Validation
Message search text and folder names must not contain ASCII control characters, including carriage returns, line feeds, NUL, tabs, or DEL. Arbiter rejects these values before opening an IMAP session, rather than removing the controls or sending them upstream. Quotes and backslashes are escaped within one IMAP argument. Message ids must contain ASCII digits, and flag names must be known system flags or valid user flag atoms.
Invalid command arguments return validation_error with the IMAP domain code
invalid_arguments. Message bodies retain their newlines and are sent as
IMAP literals when appending messages or saving drafts.
Folder Metadata
Account folder entries are overlays on top of folders that already exist on the
IMAP server. They can add descriptions and kind metadata, and they can match
literal folder names or patterns. They do not create upstream folders and do
not grant access by themselves.
imap:list_folders and imap:search_folders query the upstream server, overlay
matching account folder metadata, and hide folders denied by policy. Folder
results include name, description, kind, default, and effective
operation policy. Folder list and search results also include limit and
truncated.
Message Ids
Operations that target messages use a folder-scoped message_id. This is an
IMAP UID returned by imap:list_messages or imap:search_messages. A UID is
meaningful in the selected folder; pass the folder with the message id when
reading, moving, flagging, or deleting a message.
Attachments
Each IMAP account has an operator-owned limits.max_message_bytes, defaulting
to 50 MiB (52,428,800 bytes). It limits the entire raw email, including encoded
attachments, before full-content retrieval and MIME parsing. The client validates
RFC822.SIZE metadata, requests content with bounded BODY.PEEK, and enforces
the cap on actual response bytes, rejecting oversized literals before reading
them. Server size estimates do not reject an otherwise valid message. Reading
does not mark the message seen.
An oversized message returns HTTP 403, generic policy_denied, and IMAP
domain code message_too_large, with the configured maximum in error details.
Retry guidance is transient: no and replay: safe; increasing the limit is an
operator decision. Rejection does not change the mailbox. This IMAP limit is
independent of core artifact limits, which apply when storing an attachment.
imap:list_messages and imap:search_messages fetch message headers, flags, and
MIME structure without fetching full bodies or attachment content. Messages
larger than max_message_bytes remain visible in these results. Snippets use a
bounded BODY.PEEK of a text part, preferring plain text and falling back to HTML.
limits.max_preview_bytes defaults to 16 KiB (16,384 bytes) and bounds the total
transfer-encoded text fetched per message, including any HTML fallback.
Attachments and text nested inside attachment containers are excluded. A
normal text body marked inline remains eligible; inline parts with a filename
or Content-ID are excluded from previews as attachments. A
preview may end mid-character or mid-transfer-encoding sequence; it is a snippet,
not complete message content. Attachment-only messages have no snippet.
Metadata response framing and literals use a separate internal 50 MiB safety
budget. This budget is independent of max_message_bytes and is not a
configuration field. If an upstream response exceeds it, the operation returns
an upstream protocol error while the connection is closed before another
command is sent.
All IMAP commands, including startup, authentication, folder selection, SEARCH,
FLAGS and size queries, also have an operator-owned upstream-server response
budget. limits.upstream_server.max_response_bytes defaults to 64 MiB
(67,108,864 bytes). It counts complete response framing and literals before
large payload reads. Unsolicited responses retained by imaplib reduce the
available budget for subsequent commands; issuing another command cannot
replenish space occupied by those cached responses. An individual framing
read may consume one additional byte to detect overflow.
Overflow closes the connection before another command can be sent and returns
generic upstream_protocol_error with IMAP response_too_large, configured
max_response_bytes in details, transient: no, and replay: unknown. A command
that changes the mailbox may already have taken effect before its response
exceeds the limit; do not automatically replay it. The message and preview
limits remain independent: full-message and preview FETCHes share their
respective literal budget across all literals in that FETCH. Multiple matching
full-body responses are rejected rather than returning an arbitrary body.
These are transport byte budgets, not process-memory limits or a guarantee of containment for hostile IMAP servers. Configured IMAP servers remain trusted; sender-controlled email content remains untrusted.
List/search results include an attachments inventory from MIME metadata. Its
size is the server-reported transfer-encoded part size; obtaining the exact
decoded size requires explicit full-content retrieval. Part ids can be passed
to imap:get_attachment with the same folder and message id.
imap:get_message returns the message body plus an attachments inventory.
Each attachment entry includes a MIME-part id, filename, content type, decoded
size, disposition, content id, and whether it is inline. Attachment bodies are
not returned by imap:get_message.
Fetch attachment content with imap:get_attachment when HTTP artifact delivery
is available. The operation materializes the attachment in the server's IMAP
plugin storage and returns a one-time HTTPS content_url. Use an explicit
artifact-aware client command with that returned URL to read the artifact;
request the attachment again if a new one-time URL is needed.
For a small textual artifact only, an agent can explicitly stream the artifact to stdout:
arbiter artifact get 'https://127.0.0.1:8075/api/v1/artifacts/.../content?nonce=...' --stdout
For binary attachments, run an explicit reader command through the client so the raw artifact bytes never enter stdout. Path-based tools can use a private temp file that the client removes when the command exits:
arbiter artifact with-temp 'https://127.0.0.1:8075/api/v1/artifacts/.../content?nonce=...' -- pandoc '{}' -t plain
Stdin-based tools can receive the artifact bytes directly:
arbiter artifact with-stdin 'https://127.0.0.1:8075/api/v1/artifacts/.../content?nonce=...' -- pandoc -f docx -t plain -
When the user explicitly asks to save an attachment to a local file, use the explicit save command:
arbiter artifact save 'https://127.0.0.1:8075/api/v1/artifacts/.../content?nonce=...' ./attachment.pdf
Do not use persistent saves as the default inspection path; prefer with-temp
or with-stdin for tool processing.
Flags
imap:mark_message_read mutates the standard \Seen flag and requires
read_write access to that flag.
imap:get_message_flags shows flags that policy allows the caller to see.
imap:update_message_flags can add or remove flags only when policy grants
write access to those flags.
imap:append_message and SMTP sent-copy appends require folder_append on the
destination folder. Any flags supplied with the append, including the default
\Seen flag, also require read_write access in that folder's effective flag
policy.
imap:save_draft appends to the account's configured DRAFTS folder unless a
folder is supplied explicitly. It writes both \Draft and \Seen, so the
destination folder policy must grant folder_append, DRAFT: read_write, and
SEEN: read_write.
Static config checks warn when configured draft support is ambiguous or cannot
support save_draft. Live config checks also verify that the selected configured
Drafts folder exists on the upstream IMAP server.
Delete To Trash
Non-permanent imap:delete_message uses an accessible configured TRASH folder
when delete is allowed. Moving a message into a folder marked kind: TRASH is
also treated as a delete policy decision for the source folder.
If delete is allowed but no accessible TRASH folder can be resolved, config
checks and live checks warn that imap:delete_message will permanently delete
messages for that account.