docs: mark M5 complete, set M6 as next #234

Merged
KofTwentyTwo merged 2 commits from docs/m5-milestone-status into dev 2026-07-30 02:51:43 +00:00
3 changed files with 70 additions and 43 deletions

No files matched your search

+27 -19
View File
@@ -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).
+24 -11
View File
@@ -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),
+19 -13
View File
@@ -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 @@
<h2>Where we are · 2026-07-30</h2>
<div class="band">
<h3>Phases 0–2 complete, Phase 3 feature completeness done — M1–M4 all merged</h3>
<h3>Phases 0–2 complete, feature surface (Phase 3–M5) done — M1–M5 all merged</h3>
<p>The daemon boots gRPC-only on loopback <span class="chip">127.0.0.1:9420</span> behind a keyring-minted
bearer token. <code>IMAP → store → read → SMTP send</code> runs end-to-end over gRPC, driven by the
<span class="chip">nuncio-cli</span> reference client, tested fully offline. The legacy JSON-RPC IPC is gone,
and <span class="chip">nuncio.v1</span> is published with a byte-deterministic contract-stability golden.
Milestones M1–M4 are now complete on <span class="chip">dev</span>: Calendar, Contacts, JMAP, and live
Filters are all real, wired, and API-complete. <b style="color:var(--ink)">Next up: M5</b> (mail lifecycle
&amp; incremental sync).</p>
Milestones M1–M5 are now complete on <span class="chip">dev</span> (M1–M4 already promoted to
<span class="chip">main</span>): Calendar, Contacts, JMAP, live Filters, and mail lifecycle + incremental
sync are all real, wired, and API-complete. <b style="color:var(--ink)">Next up: M6</b> (security &amp;
release hardening).</p>
<div class="chips">
<span class="chip svc">System</span><span class="chip svc">Accounts</span><span class="chip svc">Mail</span>
<span class="chip svc">Filters</span><span class="chip svc">Calendar</span><span class="chip svc">Contacts</span>
@@ -158,6 +159,7 @@
<span class="donetag">M2 · Contacts (CardDAV)</span>
<span class="donetag">M3 · JMAP</span>
<span class="donetag">M4 · Filters made live</span>
<span class="donetag">M5 · Mail lifecycle + incremental sync</span>
</div>
</div>
@@ -226,27 +228,31 @@
</div>
</div>
<div class="m prog">
<div class="m done">
<div class="node">M5</div>
<div class="card">
<div class="chead">
<h3 class="ctitle"><span class="code">M5</span>Mail lifecycle + incremental sync</h3>
<span class="stat prog">Next</span>
<span class="stat done">Done</span>
</div>
<p class="cbody">Round out real account management: edit / delete / test-connection with TLS modes
(implicit / STARTTLS / plain) actually wired; explicit send-account selection; and <b>incremental sync</b>
(honor <code>since_state</code> instead of re-fetching the whole mailbox every time) with progress and stall timeouts.</p>
<p class="cbody">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 <code>{uidvalidity}:{uidnext}</code> checkpoint (a UIDVALIDITY
change forces a safe full re-fetch — no silent mail loss), with a per-item FETCH timeout and
<code>SyncProgress</code> events. <code>SendMessage</code> takes an explicit account id (honest
<code>AccountNotFound</code>, no silent wrong-account fallback), and a failed <code>AddAccount</code>
rolls back its orphaned keyring secret.</p>
<div class="exit"><b>Exit:</b> full account lifecycle + incremental sync, offline-E2E-proven, no fabricated IMAP fallbacks.</div>
<div class="issues"><a class="iss">#170</a><a class="iss">#169</a><a class="iss">#182</a><a class="iss">#183</a><a class="iss">#184</a></div>
<div class="issues"><a class="iss">#170</a><a class="iss">#169</a><a class="iss">#182</a><a class="iss">#183</a><a class="iss">#184</a><span class="iss">done — #194–#197 merged</span></div>
</div>
</div>
<div class="m plan">
<div class="m prog">
<div class="node">M6</div>
<div class="card">
<div class="chead">
<h3 class="ctitle"><span class="code">M6</span>Security &amp; release hardening</h3>
<span class="stat plan">Planned</span>
<span class="stat prog">Next</span>
</div>
<p class="cbody">Fail-<i>closed</i> everywhere: fix the silent empty-ciphertext data-loss path; a
signature-verifying updater that refuses to apply without checksums; zeroize key material; validate vault