feat: build the outbox remote-mutation executor (filters made live) #263

Merged
KofTwentyTwo merged 3 commits from story/m6.5-outbox-executor into dev 2026-07-31 15:55:22 +00:00
3 Commits
Author SHA1 Message Date
James Maes ddfd03e805 fix(mail): scope EXPUNGE to target UID; fail closed without UIDPLUS
A bare EXPUNGE removes every \Deleted message in the mailbox, so the
DELETE path and the no-MOVE-capability move fallback could collaterally
destroy other messages a different client had flagged \Deleted. Replace
the mailbox-wide expunge with RFC 4315 UID EXPUNGE scoped to the target
UID, gated on the UIDPLUS capability; without UIDPLUS the op fails closed
with an honest error rather than falling back to a destructive bare
EXPUNGE. Tests now assert the exact scoped 'UID EXPUNGE <uid>' command
(a bare EXPUNGE is rejected) and cover the non-UIDPLUS fail-closed case.

Also provision the webhook signing key lazily so a vault issue with that
key can no longer disable the mailbox-mutation and forward paths.

Refs #252
2026-07-31 10:53:15 -05:00
James Maes 3ce8c6813a feat(nunciod): execute outbox remote mutations for real
Replace the no-op outbox worker with a real executor that drains each
pending remote mutation against the resolved account's backend: mailbox
mutations (move/copy/flag/unflag/delete) via MailBackend::apply_mutation,
FORWARD via the SMTP MessageSender, and CALL WEBHOOK via the SSRF-hardened
dispatcher (signing key provisioned from the vault). A mutation is marked
completed ONLY on genuine success; transient failures retry up to
MAX_RETRIES then fail, permanent failures (gone message, unknown action)
fail immediately. Offline E2E proves move/flag/delete/forward/webhook end
to end and that a failed op is never marked completed.

Refs #252
2026-07-31 10:32:13 -05:00
James Maes 116d8bb3f0 feat(mail): add remote-mutation ops to MailBackend (IMAP/JMAP)
Add apply_mutation to the MailBackend trait plus RemoteMutationKind/
RemoteMutationSpec covering move/copy/flag/delete. IMAP addresses by UID
parsed from imap-uid-{uid}, guards on the folder checkpoint's UIDVALIDITY
before acting, and uses UID MOVE with a UID COPY+STORE+EXPUNGE fallback.
JMAP uses Email/set (keywords/mailboxIds patch, destroy) and confirms the
id was genuinely updated/destroyed. MockMailBackend records applied
mutations. Scripted-duplex (IMAP) and wiremock (JMAP) tests, offline only.

Refs #252
2026-07-31 10:22:06 -05:00