diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 48e3ba9..c6dbec5 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -25,22 +25,30 @@ machine (or a new LLM) picks up the work cold. ## Current state of the code (2026-07-30) - **Phases 0–2 complete.** The daemon boots gRPC-only on loopback - `127.0.0.1:9420` behind a keyring bearer token, serving six services (System, - Accounts, Mail, Filters, Export, Audit), each behind the auth interceptor. The + `127.0.0.1:9420` behind a keyring bearer token, serving eight services (System, + Accounts, Mail, Filters, Calendar, Contacts, Export, Audit), each behind the + auth interceptor. The `IMAP → store → read → SMTP send` spine works end-to-end over gRPC via the `nuncio-cli` reference client, tested offline. `nuncio.v1` is published with a contract-stability golden. -- **Phase 3 (feature completeness) is now COMPLETE — M1–M4 all merged to `dev`.** +- **The feature surface (Phase 3 through M5) is now COMPLETE — M1–M5 all merged + to `dev`** (M1–M4 already promoted to `main`; M5 promotes next). Filter correctness (3.A), Calendar (M1: CalDAV + RRULE recurrence, API vertical), Contacts (M2: real CardDAV engine + API vertical), JMAP (M3: real client wired - into Mail sync), and Filters made live (M4: engine wired into sync with a + into Mail sync), Filters made live (M4: engine wired into sync with a fire-once guard, the `account`-field bug fixed, edit/export/import/logs and - `Filters.Triage` bulk rescan on the gRPC service) are all done. -- **Next milestone: M5 — Mail lifecycle + incremental sync** (account - edit/delete/test-connection + persisted TLS mode, incremental IMAP sync with - progress + per-item FETCH timeout, explicit `SendMessage` account selection, - keyring rollback on failed `AddAccount`). Then M6 (security hardening), then - M7 (API freeze / client-readiness). + `Filters.Triage` bulk rescan on the gRPC service), and Mail lifecycle + + incremental sync (M5: account edit/delete/test-connection with persisted + `imap_tls_mode`/`smtp_tls_mode` driving the real IMAP/SMTP connection, a real + per-folder `{uidvalidity}:{uidnext}` incremental-sync checkpoint with a safe + full re-fetch on UIDVALIDITY change and a per-item FETCH timeout, explicit + `SendMessage` account selection, and keyring rollback on failed `AddAccount`) + are all done. +- **Next milestone: M6 — Security & release hardening** (fail-closed + encryption, zeroized key material, WORM/ledger key-length validation, + non-loopback gRPC bind rejection + an auth-coverage canary, a fail-closed + updater on missing `SHA256SUMS`, real CIDR/DNS SSRF checks for webhooks, + inbound HTML email sanitization). Then M7 (API freeze / client-readiness). - Full detail and the path to a client-ready API: `docs/ROADMAP.md`. ## Branches & preserved WIP @@ -94,7 +102,7 @@ GitHub milestones **M1–M7** mirror `docs/ROADMAP.md`. Each milestone holds `story`-labelled issues with a full, self-contained spec. Order: M1 → M7 (M1–M5 independent-ish feature work; M6 security; M7 the client-readiness gate). -**M1–M4 are DELIVERED — all stories merged to `dev`.** M5 is next. +**M1–M5 are DELIVERED — all stories merged to `dev`.** M6 is next. **M1 — Finish Calendar (CalDAV)** · milestone #23 · **DONE** - #185 — Add Calendar API vertical (proto, daemon, CLI, E2E) · **[PROTO]** · merged @@ -113,13 +121,13 @@ independent-ish feature work; M6 security; M7 the client-readiness gate). - #192 — Move filter edit/export/import/logs onto the Filters gRPC service · **[PROTO]** · merged - #193 — Re-expose bulk filter triage as a streaming Filters RPC · **[PROTO]** *(depends on #190)* · merged -**M5 — Mail lifecycle + incremental sync** · milestone #27 ← **next** -- #194 — Account lifecycle over gRPC (edit/delete/test) + persist TLS mode · **[PROTO]** *(prior art: `wip/unreviewed-account-tls-sync`, reference-not-trust)* -- #195 — Incremental IMAP sync with progress reporting and per-item FETCH timeout -- #196 — SendMessage selects an explicit account instead of the first configured · **[PROTO]** -- #197 — Roll back orphaned keyring secret when AddAccount fails to persist +**M5 — Mail lifecycle + incremental sync** · milestone #27 · **DONE** +- #194 — Account lifecycle over gRPC (edit/delete/test) + persist TLS mode · **[PROTO]** *(prior art: `wip/unreviewed-account-tls-sync`, reference-not-trust)* · merged +- #195 — Incremental IMAP sync with progress reporting and per-item FETCH timeout · merged +- #196 — SendMessage selects an explicit account instead of the first configured · **[PROTO]** · merged +- #197 — Roll back orphaned keyring secret when AddAccount fails to persist · merged -**M6 — Security & release hardening** · milestone #28 +**M6 — Security & release hardening** · milestone #28 ← **next** - #198 — Fail closed on encryption failure in `PayloadCipher::encrypt_text_at_rest` - #199 — Zeroize long-lived engine key material on drop - #200 — Validate WORM/ledger key length from the vault, fail closed on mismatch @@ -145,7 +153,7 @@ independent-ish feature work; M6 security; M7 the client-readiness gate). those two issues if you consider that sensitive. - Decide the eventual fate of `wip/unreviewed-account-tls-sync` (review-and-adopt vs discard) once M5 lands a clean implementation. -- Follow-ups filed during M1–M4 that are open but not blocking (not scheduled +- Follow-ups filed during M1–M5 that are open but not blocking (not scheduled into a milestone yet): #216 (calendar windowing/predicate refinement), #219 (contacts notes handling), #225 (`filter edit` silently re-enables a disabled - rule and resets `created_at`). + rule and resets `created_at`), #233 (M5 sync/SMTP robustness polish). diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 1eed593..e3eac3d 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -53,8 +53,9 @@ offline. The legacy JSON-RPC IPC is removed. The `nuncio.v1` contract is published with a byte-deterministic `FileDescriptorSet` golden guarding it, plus external-codegen docs for Swift/C#/TypeScript. -**Phase 3 — Feature completeness — is now COMPLETE**, via milestones M1–M4, all -merged to `dev`: +**The feature surface — Phase 3 through M5 — is now COMPLETE**, via milestones +M1–M5, all merged to `dev` (M1–M4 are already promoted to `main`; M5 promotes +next): - **3.A Filter correctness** — **done** (account scoping, `ON ACCOUNT`, non-ASCII offsets, `NOT IN`). @@ -72,18 +73,30 @@ merged to `dev`: honestly rejected, filter edit/export/import/logs moved onto the `Filters` gRPC service, and a `Filters.Triage` server-streaming RPC does retroactive bulk rescan. +- **Mail lifecycle & incremental sync** — **done** (M5). Account lifecycle + (`UpdateAccount`/`RemoveAccount`/`TestAccountConnection`) is real over gRPC, + with `imap_tls_mode`/`smtp_tls_mode` persisted and actually driving the real + IMAP (implicit/STARTTLS/plain) and SMTP connection. Sync is genuinely + incremental: a real per-folder `{uidvalidity}:{uidnext}` checkpoint narrows + the FETCH instead of re-fetching everything, a UIDVALIDITY change forces a + safe full re-fetch (no silent mail loss), a per-item FETCH timeout replaces + the indefinite hang, and a `SyncProgress` event is emitted. `SendMessage` + takes an explicit `account_id` (honest `AccountNotFound`, no silent + wrong-account fallback), and `AddAccount` rolls back an orphaned keyring + secret if persistence fails. -**Next up: M5 — Mail lifecycle & incremental sync** (account edit/delete/test + -persisted TLS mode, incremental IMAP sync with progress + per-item FETCH -timeout, explicit `SendMessage` account selection, keyring rollback on failed -`AddAccount`). Then M6 (security hardening), then M7 (API freeze / client -readiness). +**Next up: M6 — Security & release hardening** (fail-closed encryption, +zeroized key material, WORM/ledger key-length validation, a rejection of +non-loopback gRPC binds plus an auth-coverage canary, a fail-closed updater on +missing `SHA256SUMS`, real CIDR/DNS SSRF checks for webhooks, inbound HTML +email sanitization). Then M7 (API freeze / client-readiness). -A handful of follow-ups were filed during M1–M4 and remain open, tracked as -their own backlog items rather than blocking M1–M4's completion: calendar -windowing/predicate refinement (#216), contacts notes handling (#219), and a +A handful of follow-ups were filed during M1–M5 and remain open, tracked as +their own backlog items rather than blocking completion: calendar +windowing/predicate refinement (#216), contacts notes handling (#219), a `filter edit` bug where re-saving a rule silently re-enables it and resets -`created_at` (#225). +`created_at` (#225), and further mail sync/SMTP robustness polish identified +during M5 (#233). **Explicitly deferred** (out of scope until the backend is client-ready and each is re-justified as a genuine engine capability): OpenPGP/S-MIME E2EE (#79), diff --git a/docs/roadmap/index.html b/docs/roadmap/index.html index c09c6fb..524df65 100644 --- a/docs/roadmap/index.html +++ b/docs/roadmap/index.html @@ -76,7 +76,7 @@ /* milestone rail */ .miles{position:relative;margin-top:8px} .miles::before{content:"";position:absolute;left:19px;top:12px;bottom:34px;width:2px; - background:linear-gradient(var(--done),var(--done) 57%,var(--prog) 57%,var(--prog) 71%,var(--line) 71%);border-radius:2px} + background:linear-gradient(var(--done),var(--done) 71%,var(--prog) 71%,var(--prog) 86%,var(--line) 86%);border-radius:2px} @media (max-width:640px){.miles::before{display:none}} .m{position:relative;padding-left:52px;margin-bottom:16px} @media (max-width:640px){.m{padding-left:0}} @@ -135,14 +135,15 @@

Where we are · 2026-07-30

-

Phases 0–2 complete, Phase 3 feature completeness done — M1–M4 all merged

+

Phases 0–2 complete, feature surface (Phase 3–M5) done — M1–M5 all merged

The daemon boots gRPC-only on loopback 127.0.0.1:9420 behind a keyring-minted bearer token. IMAP → store → read → SMTP send runs end-to-end over gRPC, driven by the nuncio-cli reference client, tested fully offline. The legacy JSON-RPC IPC is gone, and nuncio.v1 is published with a byte-deterministic contract-stability golden. - Milestones M1–M4 are now complete on dev: Calendar, Contacts, JMAP, and live - Filters are all real, wired, and API-complete. Next up: M5 (mail lifecycle - & incremental sync).

+ Milestones M1–M5 are now complete on dev (M1–M4 already promoted to + main): Calendar, Contacts, JMAP, live Filters, and mail lifecycle + incremental + sync are all real, wired, and API-complete. Next up: M6 (security & + release hardening).

SystemAccountsMail FiltersCalendarContacts @@ -158,6 +159,7 @@ M2 · Contacts (CardDAV) M3 · JMAP M4 · Filters made live + M5 · Mail lifecycle + incremental sync
@@ -226,27 +228,31 @@ -
+
M5

M5Mail lifecycle + incremental sync

- Next + Done
-

Round out real account management: edit / delete / test-connection with TLS modes - (implicit / STARTTLS / plain) actually wired; explicit send-account selection; and incremental sync - (honor since_state instead of re-fetching the whole mailbox every time) with progress and stall timeouts.

+

Account lifecycle (edit / delete / test-connection) is real over gRPC, with TLS modes + (implicit / STARTTLS / plain) persisted and actually driving the IMAP/SMTP connection. Sync is now + genuinely incremental via a per-folder {uidvalidity}:{uidnext} checkpoint (a UIDVALIDITY + change forces a safe full re-fetch — no silent mail loss), with a per-item FETCH timeout and + SyncProgress events. SendMessage takes an explicit account id (honest + AccountNotFound, no silent wrong-account fallback), and a failed AddAccount + rolls back its orphaned keyring secret.

Exit: full account lifecycle + incremental sync, offline-E2E-proven, no fabricated IMAP fallbacks.
- +
#170#169#182#183#184done — #194–#197 merged
-
+
M6

M6Security & release hardening

- Planned + Next

Fail-closed everywhere: fix the silent empty-ciphertext data-loss path; a signature-verifying updater that refuses to apply without checksums; zeroize key material; validate vault