Files
sharedinbox/DB-SYNC.md
T
Thomas GüttlerandClaude Sonnet 4.6 8d8dbc33db feat: JMAP send via EmailSubmission/set; role column on Mailboxes
- sendEmail dispatches on account type: IMAP keeps SMTP+APPEND path,
  JMAP chains Email/set create + EmailSubmission/set in one API call
- Sent mailbox looked up by role='sent' from local DB so sent mail lands
  in the right folder
- JmapClient gains uploadUrl/eventSourceUrl/capabilities from session,
  supportsSubmission getter, withSubmission flag on call(), and uploadBlob()
  for attachment upload before send
- Mailboxes table gains nullable role column (schema v8); _upsertJmapMailboxes
  persists role from JMAP Mailbox/get response

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:41:21 +02:00

75 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DB Sync Status
This document covers the mail-to-database sync layer only, not the UI.
## Implemented features
### JMAP
- JMAP accounts can be stored in the database.
- JMAP endpoint discovery is implemented.
- JMAP connection testing is implemented.
- `sync_state` table stores server-side state tokens per (account, resource type).
- `pending_changes` table provides a protocol-agnostic outbound mutation queue.
- `JmapClient` fetches the JMAP Session object, extracts `apiUrl` and `accountId`,
and provides a `call()` helper for API requests.
- `syncMailboxes` for JMAP: first run uses `Mailbox/get`; subsequent runs use
`Mailbox/changes` with the stored state token.
- `syncEmails` for JMAP: first run uses `Email/query` + `Email/get`; subsequent runs
use `Email/changes`. Chains both calls in a single API request via `#ids` back-reference.
- `Email/query` pagination: cursor-based loop with `position` offset and `calculateTotal`
handles mailboxes larger than 500 emails.
- JMAP background sync worker (`_JmapAccountSync`): session → flush outbound queue →
syncMailboxes → syncEmails per mailbox → 30 s poll → repeat. Exponential backoff
5300 s on failure.
- Local mutations (flag, move, delete) on JMAP accounts are written to `pending_changes`
with an optimistic local update. `flushPendingChanges` drains the queue via `Email/set`
at the start of each sync cycle.
- Email bodies are fetched on demand via `Email/get` with `bodyValues` and cached in
`email_bodies` so subsequent opens are instant.
- `syncEmails` fetches `bodyValues` during the sync pass so bodies are cached without
a separate on-demand fetch.
- `flushPendingChanges` passes `ifInState` to every `Email/set`; a `stateMismatch`
response clears the local checkpoint and triggers a full re-sync before retrying.
### IMAP
- Background sync starts automatically for IMAP accounts.
- Mailbox lists and mailbox counters are synced into the local database.
- Email headers, flags, and attachment metadata are pulled from IMAP into the local database.
- Email bodies are fetched on demand and cached locally.
- All mailboxes (not just INBOX) are synced each cycle.
- Incremental sync: `(lastUid, uidValidity)` checkpoint stored in `sync_state`; only
new UIDs are fetched on subsequent runs; UID-validity change triggers a full re-scan.
- Deletion reconciliation: server UID set is compared against local rows; any email
absent from the server is removed from the local DB.
- Local mutations (flag, move, delete) are written to `pending_changes` with an
optimistic local update; `flushPendingChanges` drains the queue over a single
IMAP connection at the start of each sync cycle.
- Sent messages are appended to the Sent folder after SMTP delivery.
- Sync retries use exponential backoff after failures.
### Cross-protocol
- `sync_log` table records each sync cycle's account, result (ok / error), error
message, start time, and finish time. Used for debugging and "last synced" UI.
---
## Next steps
### JMAP hardening
- **JMAP send**: implement outgoing mail via `EmailSubmission/set` in addition to the
current SMTP path.
- **Push instead of polling**: upgrade `_JmapAccountSync._wait()` to use an
`EventSource` connection to the JMAP push URL when the server advertises push
capability. Fall back to 30 s polling when push is unavailable.
### Shared / cross-protocol
- **Conflict-resolution hardening**: document and enforce the server-wins policy
consistently — check `notUpdated`/`notDestroyed` per-item errors in JMAP `Email/set`
responses, handle IMAP `NO`/`BAD` gracefully, and evict changes that exceed a
maximum retry threshold (e.g. 5 attempts) to prevent queues from growing unboundedly.