Phases completed: 4 phases, 15 plans
Stats:
- Git range:
d16057c→3a7f094(117 commits; 19feat(+ 12fix() - LOC: 3,543 in
src/(11 files) · 6,407 incl.tests/(46 files) - Test suite: 86 tests green under
cargo test --features mock - Timeline: 2026-04-20 → 2026-04-22 (~48 hours, 3 calendar days)
- Requirements: 49/49 v1 requirements validated
- CI:
fmt,clippy -D warnings,nextest,audit,deny check,lycheeall green
Key accomplishments:
-
Foundation with locked wire format — Rust crate scaffold on exact cclink v1.3.0 crypto pins (
pkarr 5.0.3,ed25519-dalek =3.0.0-pre.5,age 0.11,argon2 0.5,hkdf 0.12), plainfn main()(notokio), CI green onfmt/clippy -D warnings/nextest/audit/deny;OuterRecord/OuterRecordSignableJCS fixture (outer_record_signable.bin) committed to freeze the byte-level wire format; domain-separated HKDFcipherpost/v1/<context>, Argon2id params in PHC file header, zeroize discipline across every secret-holder. -
Identity + encrypted send/receive round trip —
identity generate/showwith TTY double-confirm passphrase (fix2e29b74after UAT found single-prompt footgun), OpenSSH + z-base-32 fingerprints, mode-0600 enforcement;Envelope/Material::GenericSecretpayload schema with JCS round-trip and 64 KB plaintext cap;cipherpost send --self | --share <pubkey>publishes via PKARR,cipherpost receiveenforces outer+inner signature verify before any age-decrypt, then TTL, then the full-fingerprint D-ACCEPT acceptance screen with typed-z32 confirmation. -
Signed receipt — the cipherpost delta —
Receipt/ReceiptSignableJCS fixture (receipt_signable.bin, 424 B) committed; recipient-signed Ed25519 viaverify_strict+ round-trip-reserialize guard; post-acceptancepublish_receiptto DHT label_cprcpt-<share_ref>via resolve-merge-republish (coexists with outgoing shares, no clobber); sender-sidecipherpost receipts --from <z32> [--share-ref | --json]fetches, verifies, and renders a 10-field audit-detail view. Tamper-zero-receipts invariant enforced: any byte-flip between outer verify and acceptance aborts before publication. -
Protocol documents drafted —
SPEC.md(payload schema, JCS reference vector, outer+inner signature format, share URI, DHT labels,share_refderivation, TTL semantics, exit-code taxonomy, non-interactive passphrase contract),THREAT-MODEL.md(identity compromise, DHT adversaries incl. sybil/eclipse/replay, purpose-as-sender-attested with false-purpose example, acceptance-UX attacks, receipt replay, passphrase-prompt MITM),SECURITY.md(live-tested disclosure channel via GHSA, 90-day embargo, cclink lineage +cipherpost/v1HKDF prefix reference),lycheelink-check CI job pinned to0.21.0. -
End-to-end verifiability — 86 tests across 35 integration files, including Phase 3 SC1–SC4 (tamper-zero-receipts, share_ref filter, coexistence, two-identity E2E); cross-fixture byte-level determinism under JCS; mock
Transportlets the full happy-path + every adversarial abort run without touching Mainline DHT. IDENT-01 interactive TTY passphrase prompt verified manually on a real terminal at milestone close.
Known deferred items at close:
- Crate-pin drift —
serde_canonical_jsonshipped as 1.0.0 (planned 0.2; API matches),pkarrtransitively resolved to 5.0.4 (pinned 5.0.3), PKARR wire-budget measured at 550 bytes (planned 600). All documented in01-VERIFICATION.md; functionally correct. - Real-DHT cross-identity round trip never run — MockTransport exercises the full code path but cannot reach Mainline DHT. Documented as
reason_documentedinv1.0-MILESTONE-AUDIT.md. - PKARR merge-update race —
publish_receiptusescas(compare-and-swap), no explicit concurrent-racer test. Not triggered by walking-skeleton usage (one sender, one recipient, one accept per share). - Test advisory
GHSA-36x8-r67j-hcw6left in draft state as a permanent reproducibility record of the disclosure-channel round-trip. - Local dev lychee pinned to 0.21.0 (project rustc 1.85.1 limit); future rustc bump will allow 0.23.0+.
- Traceability-table bookkeeping — 29 rows in the archived
REQUIREMENTS.mdretained "Pending" labels although body checkboxes are checked and phase VERIFICATION reports confirm implementation. Archive reflects the live state at close. - Full milestone audit: see
.planning/milestones/v1.0-MILESTONE-AUDIT.md(statustech_debt, zero truly-unsatisfied requirements).
Phases completed: 5 phases (5–9), 24 plans
Stats:
- Git range:
eafb73f→e17616e(180 commits across 5 phases) - LOC: +9,788 / -451 across 81 files (
src/+tests/); src/ now 14 files / 6,627 LOC; tests/ now 68 files / 8,798 LOC - Test suite: 311 passed / 0 failed / 19 ignored under
cargo test --features mock(vs. 86 at v1.0 close) - Timeline: 2026-04-23 → 2026-04-26 (~3.5 calendar days)
- Requirements: 67/67 v1.1 requirements validated (PASS×9, DOC×4, X509×9, PGP×9, SSH×10, PIN×10, BURN×9, DHT×7); zero "Pending" rows survive into archive (DOC-03 traceability rule held)
- CI:
fmt,clippy -D warnings(1.85),nextest,audit,deny check,lycheeall green at HEAD-of-origin (Phase 5–9 commits local-only at close, awaiting push)
Key accomplishments:
-
Non-interactive automation E2E (Phase 5) —
--passphrase-file <path>and--passphrase-fd <n>on bothcipherpost sendandcipherpost receive; argv-inline--passphrase <value>rejected (uniform with identity subcommands). PASS-09 CI integration test (tests/pass09_scripted_roundtrip.rs) proves both fd and file paths run end-to-end without a TTY. Source-of-truth precedence locked project-wide:--passphrase-fd > --passphrase-file > CIPHERPOST_PASSPHRASE > TTY. SPEC.md gained §3.5 "DHT Label Stability" declaring_cipherpostand_cprcpt-*as wire-format constants requiring aprotocol_versionbump to change. Traceability format locked to inline phase tags (DOC-03) — eliminates the "Pending row" drift class that hit v1.0. -
Three typed payload variants —
X509Cert,PgpKey,SshKey(Phases 6–7) — Phase 6 pattern-establishesMaterial::X509Cert { bytes }with DER-canonical storage, PEM-input normalization at ingest, 8-field acceptance preview (Subject/Issuer/Serial/NotBefore/NotAfter/Key/Fingerprint/Status),--armorflag for PEM output. Phase 7 mechanically applies the pattern twice forMaterial::PgpKey { bytes }(binary OpenPGP packet stream; ASCII-armor rejected to preserve JCS byte-identity; v4/v5 fingerprint render; secret-key warning) andMaterial::SshKey { bytes }(OpenSSH v1 only; legacy PEM/RFC4716/FIDO rejected; SHA-256 OpenSSH-style fingerprint; DEPRECATED tag for DSA/RSA<2048). Three new JCS fixtures committed (X509: 626 B, PGP, SSH);cargo tree | grep ed25519-dalekpre-flight passed (no 2.x leak alongside=3.0.0-pre.5pin). MSRV 1.85 held by Cargo.lock pin totime 0.3.41after x509-parser pulledtime 0.3.47(rustc 1.88). -
--pinand--burnencryption modes — orthogonal compose over all four Material variants (Phase 8) — PIN crypto = cclink-fork-with-divergence: Argon2id(PIN + 32-byte random salt) → HKDF-SHA256cipherpost/v1/pin→ 32-byte X25519 scalar → wrapped into ageIdentityfor nested-age inner layer. No directchacha20poly1305calls — CLAUDE.md load-bearing rule preserved. Burn = single-consumption with state-ledger inversion: emit-before-mark order (D-P8-12) — emit decrypted bytes → fsync → appendstate: "burned"ledger row → fsync → touch sentinel. Receipt published unconditionally on burn-receive (BURN-04 — burn does NOT suppress attestation). Acceptance banner shows[BURN — you will only see this once]em-dash marker. PIN prompt rendered before typed-z32 acceptance. 23-test pin × burn × {GenericSecret, X509Cert, PgpKey, SshKey} compose matrix intests/pin_burn_compose.rscovers orthogonality. v1.0 byte-identity preserved viais_falseskip-serializing-if; new fixturesouter_record_pin_required_signable.bin(212 B) +envelope_burn_signable.bin(142 B) committed. Wrong-PIN folds into existingError::DecryptFailed(NO new variant); error-oracle hygiene preserved at exit 4. -
Real-DHT release-acceptance gate + CAS retry-and-merge contract (Phase 9) —
tests/cas_racer.rsBarrier-synced two-thread racer (DHT-01/02) asserts exactly one publisher wins on first attempt under contention, the loser retries-and-merges, both receipts persist in the final PKARR state — runs deterministically in CI. Single-retry-then-fail CAS contract onTransport::publish_receipt(D-P9-A1); retry lives inside the trait method (D-P9-A2);DhtTransportandMockTransportmirror identical structure.tests/real_dht_e2e.rscross-identity round trip behind#[cfg(feature = "real-dht-e2e")]+#[ignore]+#[serial]triple-gate; 7-step exp-backoff[1u64, 2, 4, 8, 16, 32, 64]with in-test 120s deadline; UDP pre-flight againstrouter.bittorrent.com:6881. CI never enables the feature.RELEASE-CHECKLIST.md(91 lines, 29 checkboxes) documents the manual real-DHT invocation as the per-release gate (D-P9-D2 + Pitfall #29).tests/wire_budget_compose_pin_burn_pgp.rsDHT-07 wire-budget composite asserts pin+burn+2KB GenericSecret produces cleanError::WireBudgetExceeded { encoded: 5123, budget: 1000 }at send. CLAUDE.md +3 load-bearing lock-ins (CAS retry contract; pkarr defaults only — noCIPHERPOST_DHT_BOOTSTRAPenv var; real-DHT triple-gate discipline). -
Solo-builder hygiene held end-to-end — Zero "Pending" rows survive into v1.1 archive. Inline phase-tag traceability (DOC-03) is canonical; phase VERIFICATION.md files are authoritative. v1.1 Coverage Summary table (67 reqs) cross-referenced against three independent sources (REQUIREMENTS inline tags + phase VERIFICATION
requirements_covered:+ SUMMARYrequirements_completed:frontmatter). 3-source check produced zero unsatisfied, zero orphaned. Audit verdict: PASSED (.planning/milestones/v1.1-MILESTONE-AUDIT.md).
Known deferred items at close:
- Wire-budget escape hatch (cross-cutting Phase 6/7/8) — realistic typed Material (X.509 234+ B DER, PGP variable, SSH variable, pin+burn-composed payloads) exceeds the 1000-byte PKARR BEP44 ceiling. Round-trip tests
#[ignore]'d behind positiveError::WireBudgetExceededclean-surface pins. Documented in SPEC.md §Pitfall #22 with measured 5123-byte composite. Deferred to v1.2+ as architecturally orthogonal to v1.1's PRD-closure scope. Candidate fixes: two-tier storage / chunking / out-of-band payload + inline hash commit. - Real-DHT cross-identity round-trip is manual-only via
RELEASE-CHECKLIST.md— Phase 9 goal satisfied by test EXISTING / COMPILING / properly triple-gated. Per-release execution is a process step, not a CI gate. Scheduled for first invocation at v1.1.0 release tag time. - Toolchain divergence —
rust-toolchain.tomlpins 1.88; CI clippy pins 1.85; clippy 1.88 enforcesuninlined_format_argsas default lint, surfacing 65+ instances locally that 1.85 doesn't catch. CI is source-of-truth release gate per CLAUDE.md MSRV-1.85;build.rs:17(the specifically-flagged blocker in PROJECT.md and09/deferred-items.md) was fixed in commite45347bat milestone close. Reconciliation deferred to v1.2 maintenance pass. - Code-review advisories (non-blocking): WR-01 (
tests/real_dht_e2e.rs:153propagation wait routes viaalice_transport; should resolve viabob_transportto avoid local pkarr cache short-circuit) and WR-02 (MockTransport::publishdoesn't bumpseq; latent concurrent-write data-loss window dormant in v1.1, matters only at future composition). - Fixture-regen reproducibility across OpenSSL versions (Phase 6) — documentation-promise (
tests/fixtures/x509_cert_fixture.reproduction.txt); no automated test asserts non-drift. Accepted as deferred. - Non-interactive PIN input (
--pin-file/--pin-fd) — explicitly deferred to v1.2+ per DEFER-PIN; PIN is intentionally human-in-the-loop second factor. - Destruction attestation workflow (originally PRD v1.1) — shifted to v1.2+ because v1.1 filled up with PRD-closure scope.
- Full milestone audit: see
.planning/milestones/v1.1-MILESTONE-AUDIT.md(statuspassed, 67/67 requirements satisfied, no critical blockers).
Wire break: PROTOCOL_VERSION 1 → 2; crate 1.2.0-alpha.1 → 2.0.0-alpha.1 (semver major — v1.1 and v2 URIs are mutually NotFound because they derive different keys). Tracked ad hoc post-GSD (reviewer findings verified then fixed, atomic commits to main); git range 7f1fbcf → b6c1122.
Summary: Lifted the v1.1 one-record-per-key packet-budget ceiling by publishing every share and every receipt under its OWN key derive(parent_pub, share_ref) — single-hop stealth blinding (t = reduce_mod_ℓ(SHA-512("cipherpost/v2/derive-addr" ‖ A ‖ raw16(share_ref))), A' = A + t·G; pkarr cannot sign under a blinded seed-only key, so packets are BEP44 hand-signed via ed25519-dalek::hazmat::raw_sign, self-verified, and assembled through the public from_relay_payload). Consequences now live: many outstanding shares per sender and many receipts per recipient; self-receipts re-enabled (D-SEQ-06); cipherpost receipts requires --share-ref because receipts are no longer enumerable from a public identity (a privacy win — the derived key is blinded by share_ref); slim receipt schema {accepted_at, ciphertext_hash, cleartext_hash, protocol_version, share_ref, signature} with verify_receipt taking the recipient pubkey as verify context (nonce + both pubkeys dropped). The v1.1 parent-key Transport surface (publish/resolve/publish_receipt/resolve_all_cprcpt) and its merge-republish + CAS + seq machinery were deleted (~765 LOC net removed); tests/real_dht_e2e.rs was ported to a derived-key round trip. Docs fully aligned to v2: SPEC (new §3.8 derivation + §8.4 byte-exact golden vector, §3.4 slim receipt), THREAT-MODEL (§1/§7 receipt-privacy rewrite + the now-eliminated concurrent-publish race), README, FAQ, SECURITY, and CLAUDE.md. Full suite green under cargo nextest run --all-features; cargo fmt --check + cargo clippy --all-targets --all-features -- -D warnings clean; CI green at HEAD.
Reference / proof: docs/design/derived-key-addressing.md (status IMPLEMENTED, all phases DONE) with byte-exact golden vectors pinned in src/derive.rs::golden_vector_seed7_ref11 and tests/derived_key_spike.rs; SPEC.md §3.8 / §8.4.
Real-DHT evidence (produced 2026-08-02): the v2.0.0-alpha.1 tag push triggered .github/workflows/release-acceptance.yml, which ran the cross-identity round trip against live Mainline DHT — PASS in 739s (share published + resolved under derive(alice_pub, share_ref), receipt under derive(bob_pub, share_ref); no mock, well within the 900s deadline). Full record: RELEASE-EVIDENCE-v2.0.0-alpha.1.md + CI artifact real-dht-evidence-v2.0.0-alpha.1 (run 30725213381). No v2-MILESTONE-AUDIT.md exists (GSD retired; this entry + the design doc + the evidence file are the close record).