Synchronization
Synchronization is a read-only import of every new message from an enabled mailbox's IMAP INBOX, one commit per message.
Overview#
Synchronization is a read-only import of every new message from an enabled mailbox's IMAP INBOX, one commit per message.
One pass#
- Load the mailbox and resolve its selected Skarbiec credential.
- Open a direct-TLS IMAP connection.
- Authenticate with username/password or Gmail XOAUTH2.
- Select
INBOXand searchUID max(last_uid + 1, 1):*. - Sort UIDs and fetch each with
BODY.PEEK[]. - Normalize each message whole and insert it by
(mailbox_id, UID)in its own commit, moving the cursor past it. - Persist the successful completion timestamp and clear the mailbox error even when the provider returned no new UID; a failure part way keeps what was committed.
A single synchronization pass does not wait for new mail. Enabled mailbox watchers use IMAP IDLE between passes. Folder selection beyond INBOX, flag mutation, deletion, and attachment download are unavailable. Neither the IMAP nor the SMTP client sets a network timeout; each waits for the server's answer or the connection's own error.
Output#
A single-mailbox sync returns mailbox_id, received, skipped, last_uid, and completed_at, or fails with one error envelope after keeping every message committed before the failure. completed_at is the persisted mailbox last_sync_at, including a successful empty pass: that pass preserves the cursor and messages but clears any earlier mailbox failure. A database failure while recording completion refuses synchronization rather than returning an invented timestamp. An all-mailbox sync returns a top-level completion time and one result per enabled mailbox; a mailbox failure is embedded with ok:false and does not make the command fail or stop later mailboxes. The CLI, authenticated API and Desktop Sync use the same recorded outcome.
The real tests/mailbox/backlog.mjs journey imports a dedicated provider backlog and synchronizes again through the same real resolver and provider. Its empty-pass case requires no new or skipped messages, unchanged cursor and retained messages, empty error fields, and a new reported completion time read back from the mailbox. Commands, exit statuses, candidate identity and mailbox observations are retained under .build/real-tests/mailbox-backlog. A definition is not a passing run; unrelated incoming mail refuses the empty-pass qualification rather than silently skipping it. This journey does not yet exercise recovery from a persisted provider or credential failure.
Scheduling#
serve synchronizes each enabled mailbox and waits for its provider's IMAP IDLE notification before synchronizing again. There is no process poll setting or mailbox poll interval. Failed watchers expose the mailbox error and leave their watch loop; the service can retry them on a later watch round. One in-process operation lock serializes synchronization, reply, and outbound send work; concurrent processes on one database are unsupported.
An IMAP_IDLE_FAILED error names the mailbox address, configured IMAP host and port, and the persisted UID after which the watcher was waiting, followed by the actual provider or connection error. Read the saved cause through skrzynka mailbox show <id>, the authenticated mailbox API, or the Desktop mailbox failure display. A failed notification connection does not erase imported messages or advance the cursor, and the diagnostic does not claim that another synchronization succeeded.
Cursor semantics#
The cursor advances to the highest UID considered in the batch, including bounded messages that were skipped because the provider omitted a body, exceeded 2 MiB, or failed normalization. It advances only after inserts complete and success is recorded. A connection, authentication, search, or fetch failure records the normalized mailbox error and leaves last_sync_at unchanged.
Refusals#
IMAP_UNAVAILABLE— TLS connect failed; the message retains host, port and transport cause.IMAP_AUTHENTICATION_FAILED— LOGIN or XOAUTH2 failed; the message retains the provider or transport cause with the submitted secret removed. A transport failure is not proof of rejected credentials.IMAP_INBOX_UNAVAILABLE— SELECT INBOX failed; the message retains host, port and provider cause.IMAP_SEARCH_FAILED— UID search failed; the message retains the starting UID, host, port and cause.IMAP_FETCH_FAILED— fetch failed; the message retains the requested UID, host, port and cause. Earlier committed messages remain.- Credential-resolution failures occur before the IMAP connection and are recorded on the mailbox.