Skip to main content

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.