Skip to content

Feature: secret-keys #626

Closed
Closed

Description

@amritk

Tracking run for #430: move the TLS and QUIC key-holding structs onto nish:secret. This issue is the run's durable ledger; the lead re-reads it on every wake.

Run

  • repo: amritk/nish
  • base: main @ b515c1b (run start); stage 1 merged at ab9099e
  • plan: ccr-697be0c1-yzt4rm → .claude/plans/secret-keys.plan.md
  • lead session: session_01Fthkt4ZgvqkZpW7Gqdk3Po
  • flags: max-slices 4, max-rounds 10, autonomous merge (squash)
  • verification: npm run check · npm run lint · node tests/run.js (undegraded, skip count read) · node tests/run.js wasi · node tests/run.js performance · interop.yml green. No coverage command exists; the repo's definition of done is the gate (signed off).
  • decisions (signed off): Secret<u8[]> at the derivation boundary + volatile wipe in holders (no language change); AesKey's H moves to a u64[]; QUIC-5 included as stage 5; autonomous merge approved on an all-crypto plan, so merge-gate condition 6 ("crypto" sensitive path) is treated as covered by that sign-off
  • bounds: deadline 2026-10-16T14:30Z (extended by the owner on 10-16; the original 2026-10-13T02:12Z lapsed during a usage-limit outage) · no-branch 45m · no-commit 90m · re-runs 1 per check per head · check-ins 15→30→60m on no drift

Acceptance

# criterion static qa state
1 docs/security/tls.md and quic.md show TLS-1, TLS-2, QUIC-1, QUIC-2, QUIC-5 Closed (or narrowed to what no wipe reaches), rows anchored at a commit, index rows agree; #430 closed — — pending
2 node tests/run.js survive passes a named -O2 check per wipe root (aesKeyWipe, ChaCha, record protection, TlsServer/TlsRecordServer, QuicKeysSlot, key update, connection end, listener/server release) — — pending
3 A run-time test prints every wiped array (round keys incl. kernel H powers, H, ChaCha state, TLS and QUIC keys, server keys) as zero after close, fail, restart, level discard, key update and release — — pending
4 No existing tests/link/*/expected.out changed across the run, and interop.yml was green on every merged head — — pending
5 A plain u8[] handed to a migrated derivation is a compile error — — pending
6 AEAD-4 says rk (H's powers included) and H are now volatile-wiped when TLS and QUIC drop a key, and that the vector registers stay out of reach — — pending
7 On merged main: npm run check, lint, undegraded npm test, wasi and performance green — — pending

Slices

stage owns model depends on session branch PR CI rounds state
crypto-wipe std/crypto/{aes,chacha20poly1305,hkdf}.ts, tests/run.js, tests/wipes/**, tests/link/crypto_* lead — session_01AcLwjeFfW9KZUKr1gPeTBu feature/secret-keys/crypto-wipe #639 green 3/10 merged (ab9099e)
tls-holders std/net/tls.ts, std/net/tls/{record,record-server}.ts, std/net/tls-tcp.ts, tests/wipes/tls.js, tests/link/net_tls_wipes lead crypto-wipe session_01Wi8YBaK8P1QyXQeQqvrXNR (spawned 03:36Z 10-16) feature/secret-keys/tls-holders — — 0/10 running
quic-holders std/net/{quic,quic-packet}.ts, tests/wipes/quic.js, tests/link/net_quic_wipes lead crypto-wipe session_01X5Sq739k47sBrSihpS3T73 (spawned 03:36Z 10-16) feature/secret-keys/quic-holders — — 0/10 running
secret-boundary std/net/tls/schedule.ts, std/net/quic-packet.ts, tests/link/net_{tls,quic}_*, records lead tls-holders, quic-holders — feature/secret-keys/secret-boundary — — — pending
quic-server-keys std/net/{quic-listener,http3-server}.ts, tests/wipes/quic-server.js, tests/link/net_quic_server_wipes lead quic-holders (merge after secret-boundary) — feature/secret-keys/quic-server-keys — — — pending
acceptance — sonnet all — — — — — pending

Review ledgers

  • fix(crypto): wipe AES key schedules, H and ChaCha state with the volatile wipe #639 rejected (never re-raise): wiped key still seals under zero schedule (documented); stale hHi/hLo in crypto-aead.md / ct-verification.md (stage 4 owns); fallback wipes inside bitsliced bodies (by design); empty wipe-area files register no check (by design); kernel tail not pinned (read-back walks all of roundKeys); h[] index NL9007 (perf gate green); aesHeaderMask block unwiped (public-input PRP output); counts blind to target buffer (by design); AEAD regex matches checks module (harmless); panic tests don't pin message (repo convention); ChaCha "what is wiped" doc (accurate).

Log

Activity

  1. amritk commented on Oct 12, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-12T21:10Z: #639 round 1 at bbc887f

    CI was 26/26 green (interop included). Two pr-adversary shards ran, then one verifier.

    • Confirmed: 3, sent to the worker as one batch.
      • The GCM path's working copies are unwiped: aesCtrInto buf/q and aesGcmTag mask.
      • Two pointers to the moved -O2 check are stale: hmac.ts:34 and crypto-k1.md:91.
      • The SHA-384 Secret short-PRK panic is untested.
    • Rejected (refuted by the verifier):
      • The kernel tail is not pinned. Refuted: the read-back walks all of roundKeys.
      • The new h[...] index sites cause NL9007. Refuted: the performance gate holds std/ to zero and is green.
    • Rejected (lead, from the plan):
      • net_quic_memory/units.ts is out of Owns. It is in Owns.
      • The fallback wipes sit inside the seam bodies. The plan intends this.
      • AEAD-4 still has stale hHi/hLo text. That belongs to secret-boundary.
      • A wiped key still seals under a zero schedule. This is documented, not required by the plan.
      • Empty area files register no check. This is by design.

    Ledger row crypto-wipe: rounds 1/10, state fixing.


    Generated by Claude Code

  2. amritk commented on Oct 12, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-12T21:38Z: #639 round 2 at 07c940d

    CI was 26/26 green. Two fresh pr-adversary shards ran, then one verifier.

    • Confirmed: 2, sent to the worker as one batch.
      • The refusal-path wipe(state) in chacha20Poly1305Open is not pinned. opt merges it, and the verifier reproduced this with LLVM 18.
      • For a non-12-byte IV, aesGcmJ0's y and j0 are unwiped, and they are H-derived.
    • Rejected (refuted by the verifier):
      • The aesHeaderMask block is unwiped. Refuted: it is a public-input PRP output.
      • The counts are blind to which buffer is wiped. Refuted: by design, and the fix is disproportionate.
    • Rejected (lead, at dedupe):
      • A wiped key still seals. Already rejected in round 1.
      • Stale hHi/hLo text in crypto-aead.md and ct-verification.md. Stage 4 owns it; ct-verification.md:312 has been added to stage 4's list.
      • Stale comments in record.ts. Stage 2 owns them.
      • K1-7 does not list the label Secret twins. Stage 4 owns it.
      • ChaCha's per-call state is not read back. Not possible: it is a local that dies with the call.
      • The only filter now matches more. Harmless.
      • The AEAD regex also matches the checks module. Harmless.
      • The panic tests do not pin the message. That is the repo's link-harness convention.

    Ledger row crypto-wipe: rounds 2/10, state fixing.


    Generated by Claude Code

  3. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-16T01:45Z: the run resumes after a usage-limit outage

    The lead and the worker were both stopped by the account's weekly usage limit from about 2026-10-12T22:00Z until 2026-10-16T01:25Z. The run's deadline (2026-10-13T02:12Z) passed during that outage.

    The crypto-wipe worker resumed and pushed b97ce50, which addresses review round 2. CI is 26/26 green on it, interop included.

    No new review round has started, because the deadline has passed. The lead is asking the owner whether to extend it.


    Generated by Claude Code

  4. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-16T02:32Z: deadline extended by the owner

    The owner extended the run's deadline by 12 hours. New deadline: 2026-10-16T14:30Z. The original deadline had lapsed during the usage-limit outage.

    #639 at b97ce50 addresses round 2. CI is 26/26 green and it is conflict-free after merging main 6729c7c. Review round 3 starts now.


    Generated by Claude Code

  5. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-16T03:25Z: #639 round 3 at b97ce50 has converged

    Two fresh pr-adversary shards ran, then a verifier. Zero findings were confirmed.

    Rejected at dedupe:

    • The aesHeaderMask block. Already refuted in round 2.
    • Stale hHi/hLo text in crypto-aead.md and ct-verification.md. Stage 4 owns it.
    • The AEAD module regex. Already rejected in round 2.
    • Kernel-tail coverage on soft runners. Already refuted in round 1.
    • Scope of hmac.ts, crypto-k1.md, quic-packet.ts and record.ts. The lead permitted these edits.
    • Harness edge cases. Nothing currently triggers them.

    Refuted by the verifier:

    • The ChaCha "what is wiped" doc overstates. It does not: every array that sentence names is wiped.

    Merge gate status:

    Ledger row crypto-wipe: rounds 3/10, state converged, waiting on the merge of main.


    Generated by Claude Code

  6. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-16T04:09Z: check-in

    • tls-holders: branch pushed at 4eb357c. The stage is coded; the full suite, the measurement and the PR are still ahead.
    • quic-holders: branch pushed at 85c9420 as a WIP commit. The full suite is running.

    No PRs yet. Both workers are healthy.


    Generated by Claude Code

  7. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-16T04:41Z: check-in

    tls-holders (4eb357c)

    • The stage is coded. The worker had ended its turn waiting for a reply, so the lead told it to continue to a PR.
    • It edited one line outside its Owns: refill(...) in tests/link/net_tls_memory/checks.ts. This is permitted, because wiping on fail invalidated a check's reused key array. That test's expected.out is unchanged.
    • Routed gap: quic.ts's wipeAll does not call the new TlsServer.wipeResumption(). It goes to the first stage that owns quic.ts after tls-holders merges: secret-boundary, or quic-holders if it is still open then.

    quic-holders: advanced to 1a6628d.

    main moved to 1198e0a. No PRs yet.


    Generated by Claude Code

  8. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-16T06:53Z: both stage PRs are open

    The lead is subscribed to both. CI is running. Round 1 on each starts once its head is green and mergeable.

    Merge order: whichever PR converges first merges first. The other then merges main and regenerates its goldens.


    Generated by Claude Code

  9. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-16 ~07:45Z: review round 1 on tls-holders (#652) and quic-holders (#653)

    Each PR had two reviewer shards and one verifier. The verifier built at the PR head and deleted wipe lines one at a time.

    #652, reviewed at 1087b69, posted at b724581. b724581 is a merge of main plus regenerated goldens only; I diffed it against main to confirm.

    • 4 items confirmed:
      • 8 wipe lines that no -O2 root pins: start, advance ×4, open nonce, sign master and handleFinished.
      • Read-back gaps: the preconditions are summed rather than per array, there is no KeyUpdate read-back, and there is no pre-OPEN failure read-back.
      • Small test fixes: a check name that overclaims, and an fd leak.
      • Stale tls.md paragraphs.
    • Refuted: pump and TlsRecordProtection.advance are unpinned (they are reached through TlsTcpServer.close); late-Finished resumption is a defect (documented behaviour).

    #653, reviewed and posted at ba849c5. The worker is pushing a merge-main commit, dd5117b.

    • 5 items confirmed:
      • Blocking, visible on the wire: the closing period can end before CONNECTION_CLOSE is taken, so the close is lost. A demo program showed sentClose=false after a gap of about 10 PTO.
      • 8 unpinned wipes.
      • The key-update read-back passes with its wipe deleted.
      • reset is never driven, and qwConnZero has gaps.
      • The QUIC-2 record overclaims, and its anchor commit predates the rewritten row.
    • Refuted: "key update is one direction only" is a defect; the afterTls count of 86 is unexplained; the std/README.md and http3.md cross-refs are stale.

    Rejected ledger additions (do not raise again):

    • The late-Finished resumption wipe ordering.
    • The http3.md and std/README.md QUIC-2 pointers.
    • Key-update direction coverage.

    Both workers have been told to fix every item in one push and report the new head. Round 2 starts on each PR when its fix lands.


    Generated by Claude Code

  10. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-16 ~10:05Z: review rounds 2 and 3

    #653 (quic-holders)

    • Round 2 (43dc940). All round-1 items are fixed. The closing period now starts in takeCloseInto. Every close path was traced, and no close is lost and nothing on the wire changes. The 67 wipe lines are pinned across 20 roots.
    • Round 2 confirmed 4 record-wording items:
      • the QUIC-2 "never released" clause;
      • a close is sent only once;
      • QUIC-1 and its README row are stale;
      • the read-back claims overreach.
    • Refuted: the clock-gap test only checks that a datagram was sent. This is wrong, because only the CONNECTION_CLOSE can go out in CLOSING.
    • Also refuted: the claim that the cidSeed and ephemeral wipes are unobserved.
    • The fixes were docs-only in 7b9f3b1, which I diffed: 3 lines across 2 files.
    • Round 3 (7b9f3b1). No code defects. 3 wording items confirmed:
      • "which the listener uses" is false;
      • the Tests sentence and the PR body still group the reset tokens and parameters under the idle, closing and draining ends;
      • a count comment in tests/wipes/quic.js names a non-existent wipe on the failure path.
    • A docs-only fix was requested.

    #652 (tls-holders)

    • Round 2 (2862f8d). Round 1 is fixed and the code has no defect. Every wipe line I deleted moved an -O2 count.
    • 6 record-wording items confirmed:
      • the read-back paragraph overclaims, including for handleFinished, whose wipe is pinned only by its -O2 count;
      • "Result." gives the wrong open count;
      • restart is conflated with start;
      • "every key wiped at end" is unqualified;
      • tlsNextTrafficSecret is missing from TLS-1;
      • the ab9099e anchor.
    • Refuted: KeyUpdate precondition asymmetry, and secureZero(next) being unpinned (deleting it fails 2 read-backs).
    • A docs-only fix was requested.

    Additions to the rejected ledger

    • Precondition code beyond what the record claims: the record is narrowed instead.
    • No backstop for a close that is owed and never taken: by design, now documented.
    • QW_APP_WRITE_SLOTS is duplicated.
    • reset uses zero entropy.
    • The clock-gap test asserts sent === 1.
    • std/README.md's quic-packet row: owned by stages 4 and 5.

    Generated by Claude Code

  11. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-16 ~10:40Z: stage 2 (tls-holders) merged as #652 → b322a4b

    Merge gate:

    • Review converged. Round 3 found one wording line, which was fixed in 54b2512; I verified the diff myself.
    • CI: 26/26 green on 326440e, including quic-interop-runner (ngtcp2 and quic-go), curl HTTP/3, Chrome WebTransport and h2spec.
    • Mergeable: clean, and main at 464f41e is in the head.
    • Scope: within Owns, plus the agreed net_tls_memory line and the generated goldens.
    • Squashed with the PR's proposed changelog message.

    Records:

    • TLS-2 is closed.
    • TLS-1 is narrowed.
    • The docs/security/README.md Total is now 81 fixed and 19 open Lows.

    Next steps:


    Generated by Claude Code

  12. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-16 ~11:23Z: stage 3 (quic-holders) merged as #653 → e21e509

    Merge gate:

    Records:

    • QUIC-2 is closed.
    • QUIC-1 is narrowed to the allocating key forms.
    • Index: 82 fixed, 18 open.

    Running now:

    • secret-boundary (stage 4, session_018gySd6itL5j4UPeVuAAjXa): the TLS half is done and pushed. It is merging main for the QUIC half.
    • quic-server-keys (stage 5, session_01URstA1Mewe8kiMxysyqKRo): spawned. Its PR carries Fixes #430.

    The quic-holders worker is archived.


    Generated by Claude Code

  13. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-16 ~12:25Z: round 1 on stages 4 and 5 (reviewed on the branches before the PRs opened, because of the deadline)

    Stage 4, secret-boundary @ 30320ee (two shards)

    • No code defect in the Secret handling. Every cited check name exists, and the -O2 counts add up.
    • Scope accepted by the lead:
    • Sent to the worker:
      • An API trap: quicKeysUpdate shares hp with its input. The preferred fix is to copy it.
      • Test copies of secrets that are never wiped, where the copy has a clear last use.
      • Record overclaims:
        • AEAD-4 says "whenever".
        • TLS-1 says tlsMasterSecretInto wipes the master secret.
        • The K1-7 and K1 addendum wording.
        • "any of them refused".
      • Smaller items: a module-header example that doesn't compile, a Scope row, and the guide page.

    Stage 5, quic-server-keys @ 36b0c73

    • No blocking defect.
    • Sent to the worker:
      • The last reset token or Retry MAC survives release()/stop() in the reply buffers. The preferred fix is to wipe them.
      • Vacuous read-backs and a QUIC-5 Tests sentence that claims too much. Fix by narrowing the wording.
      • QUIC-2 and QUIC-5 point at each other about quicStatelessResetToken.
      • stop() zeroes a shared config in place. Document one config per server, since other users would otherwise forge under a zero key.
      • After release(), only handle is refused.
      • Scope rows. The http3.md sentence edit is approved.

    Rejected:

    • The test-only qwOneShot early-return leak.
    • The one-shot check reading back as non-zero rather than checking the value.
    • Negatives for every migrated function (one or more were required, and there are two).

    Both workers have been told to make one push, open the PR, and not merge. Deadline 14:30Z.


    Generated by Claude Code

  14. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-16 ~13:56Z: stage 5 (quic-server-keys) merged as #664 → 33bd848

    Merge gate:

    • Review converged after round 2 at abc4458 (no findings).
    • The final head 6729c8e adds one std/README line and a merge of main. I verified the net diff against the reviewed change: hunk offsets only.
    • CI: all 30 runs green, including interop. The first no attribution failure was superseded by two later passes after the footer was stripped.
    • Mergeable: clean.
    • PR body: Part of #430. It does not close Move the QUIC and TLS key-holding structs onto nish:secret #430 because stage 4 is not in yet.
    • Squashed with the PR's proposed changelog message.

    Records:

    • QUIC-5 is closed.
    • Http3Server.stop / QuicListener.release are documented, including the hazard of sharing one config across servers.

    Order change: stage 5 landed before stage 4. Its CI went green first, and the plan's ordering was only about which PR carried Fixes #430, now resolved by hand.

    Stage 4 (#665):

    The quic-server-keys worker is archived.


    Generated by Claude Code

  15. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Log, 2026-10-16 ~14:17Z: stage 4 (secret-boundary) merged as #665 → 35d3771. All five stages are on main.

    Merge gate:

    stage PR merge
    crypto-wipe #639 ab9099e
    tls-holders #652 b322a4b
    quic-holders #653 e21e509
    quic-server-keys #664 33bd848
    secret-boundary #665 35d3771

    Open for the owner:

    Acceptance (phase 7) is running on merged main. All worker sessions are archived.


    Generated by Claude Code

  16. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Acceptance (phase 7) on merged main @ 35d3771

    # criterion result
    1 TLS-1, TLS-2, QUIC-1, QUIC-2 and QUIC-5 closed or narrowed; index agrees; #430 closed PARTIAL: TLS-2, QUIC-2 and QUIC-5 are Closed. TLS-1 and QUIC-1 are Open, narrowed to what a Secret cannot hold (NL2430): an open connection's field-held secrets, and a QuicKeys the caller does not wipe. The README index, area rows and Total (fixed 3/10/13/83, open 0/1/3/17) all agree. #430 is left open for the owner.
    2 Named -O2 check per wipe root PASS: node tests/run.js survive 25/0. All 9 wipe groups pass: crypto, HMAC/HKDF, P-256, tls ×2, quic ×2, quic-server, and both runtime-aes.c checks. Every named area is covered.
    3 Run-time read-back of every wiped array PASS: crypto_aes_wipes 45/0, net_tls_wipes 129/0, net_quic_wipes 170/0, net_quic_server_wipes 213/0. Each has an _f64 twin, and all pass.
    4 No existing expected.out changed; interop green on every merged head PASS, with one accepted exception: the run's PRs only add expected.out files, except #665's additive edit to net_quic_wipes (a #653 file; +12 checks and the tally). The two other modified files (std_json_conformance, std_json_typed) came from unrelated JSON PRs (#622, #630, #631). Interop was green on all five merged heads.
    5 Plain u8[] to a migrated derivation is a compile error PASS: reject_secret_tls_plain_key and reject_secret_quic_plain_key pass (reject_secret 95/0).
    6 AEAD-4 note PASS: rk (H's powers included) and H are volatile-wiped by aesKeyWipe when TLS and QUIC wipe a key. The vector registers and their spills are recorded as unreached.
    7 check, lint, undegraded npm test, wasi and performance green on main PASS:
    • check 0, lint 0.
    • npm test 19,464 passed, 0 failed, 1 skipped (the container lacks aarch64 crt1.o), with no DEGRADED banner.
    • wasi 39/0 with no skips.
    • performance 207/0, and the src/ ratchet passes.

    Owner decision pending: whether TLS-1 and QUIC-1 "Open, narrowed" satisfies #430. Their remainders exist only because a Secret may not be a field (NL2430), and lifting NL2430 was out of scope. Close #430 as is, or open a follow-up for NL2430.


    Generated by Claude Code

  17. amritk commented on Oct 16, 2026

    @amritk
    OwnerAuthor

    Close-out: owner decision recorded. #430 stays open. Follow-up #668, which lifts NL2430 so a Secret can be a field, will close TLS-1 and QUIC-1. Acceptance criteria 2–7 pass; criterion 1 is partial and waits on #668. All five stage PRs are merged and every worker session is archived. Closing this run ledger.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions