docs: mark M5 complete, set M6 as next #234
No files matched your search
+27
-19
@@ -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
@@ -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
@@ -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
|
||||
& 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 &
|
||||
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 & 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
|
||||
|
||||
Reference in new issue
Block a user