Repository navigation
Feature: secret-keys #626
Copy link
Copy link
Closed
Labels
Description
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 + volatilewipein holders (no language change); AesKey's H moves to au64[]; 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
- 2026-10-12T18:12Z plan signed off; ledger opened
- 2026-10-12T18:14Z spawned crypto-wipe
- 2026-10-12T20:45Z fix(crypto): wipe AES key schedules, H and ChaCha state with the volatile wipe #639 opened; conflict + no-attribution handed back
- 2026-10-12T21:10Z fix(crypto): wipe AES key schedules, H and ChaCha state with the volatile wipe #639 round 1: 3 confirmed → worker (see comment)
- 2026-10-12T21:38Z fix(crypto): wipe AES key schedules, H and ChaCha state with the volatile wipe #639 round 2: 2 confirmed → worker (see comment)
- 2026-10-12T22:00Z → 2026-10-16T01:25Z usage-limit outage; run stalled
- 2026-10-16T02:32Z owner extended deadline to 2026-10-16T14:30Z
- 2026-10-16T03:25Z fix(crypto): wipe AES key schedules, H and ChaCha state with the volatile wipe #639 round 3 converged (0 confirmed); main moved (fix(checker): let a scoped task read a shared Map or Set, and let only the first panic of a process print #621) → merge-main handed back
- 2026-10-16T03:35Z fix(crypto): wipe AES key schedules, H and ChaCha state with the volatile wipe #639 head 454b540 verified identical to reviewed b97ce50 outside regenerated goldens; CI 26/26 green; mergeable clean → squash-merged as ab9099e
- 2026-10-16T03:36Z spawned tls-holders (session_01Wi8YBaK8P1QyXQeQqvrXNR) and quic-holders (session_01X5Sq739k47sBrSihpS3T73) in parallel on ab9099e
Reactions are currently unavailable
Activity
amritk commentedon Oct 12, 2026
Log, 2026-10-12T21:10Z: #639 round 1 at bbc887f
CI was 26/26 green (interop included). Two
pr-adversaryshards ran, then oneverifier.aesCtrIntobuf/q andaesGcmTagmask.hmac.ts:34andcrypto-k1.md:91.Secretshort-PRK panic is untested.roundKeys.h[...]index sites cause NL9007. Refuted: the performance gate holdsstd/to zero and is green.net_quic_memory/units.tsis out of Owns. It is in Owns.hHi/hLotext. That belongs to secret-boundary.Ledger row crypto-wipe: rounds 1/10, state fixing.
Generated by Claude Code
amritk commentedon Oct 12, 2026
Log, 2026-10-12T21:38Z: #639 round 2 at 07c940d
CI was 26/26 green. Two fresh
pr-adversaryshards ran, then oneverifier.wipe(state)inchacha20Poly1305Openis not pinned. opt merges it, and the verifier reproduced this with LLVM 18.aesGcmJ0'syandj0are unwiped, and they are H-derived.aesHeaderMaskblock is unwiped. Refuted: it is a public-input PRP output.hHi/hLotext in crypto-aead.md and ct-verification.md. Stage 4 owns it; ct-verification.md:312 has been added to stage 4's list.Secrettwins. Stage 4 owns it.onlyfilter now matches more. Harmless.Ledger row crypto-wipe: rounds 2/10, state fixing.
Generated by Claude Code
amritk commentedon Oct 16, 2026
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
amritk commentedon Oct 16, 2026
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
amritk commentedon Oct 16, 2026
Log, 2026-10-16T03:25Z: #639 round 3 at b97ce50 has converged
Two fresh
pr-adversaryshards ran, then averifier. Zero findings were confirmed.Rejected at dedupe:
aesHeaderMaskblock. Already refuted in round 2.hHi/hLotext in crypto-aead.md and ct-verification.md. Stage 4 owns it.Refuted by the verifier:
Merge gate status:
git merge-treereports a conflict.Ledger row crypto-wipe: rounds 3/10, state converged, waiting on the merge of main.
Generated by Claude Code
amritk commentedon Oct 16, 2026
Log, 2026-10-16T04:09Z: check-in
No PRs yet. Both workers are healthy.
Generated by Claude Code
amritk commentedon Oct 16, 2026
Log, 2026-10-16T04:41Z: check-in
tls-holders (4eb357c)
refill(...)intests/link/net_tls_memory/checks.ts. This is permitted, because wiping on fail invalidated a check's reused key array. That test'sexpected.outis unchanged.quic.ts'swipeAlldoes not call the newTlsServer.wipeResumption(). It goes to the first stage that ownsquic.tsafter 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
amritk commentedon Oct 16, 2026
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
amritk commentedon Oct 16, 2026
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 atb724581.b724581is a merge of main plus regenerated goldens only; I diffed it against main to confirm.start,advance×4,opennonce,signmaster andhandleFinished.pumpandTlsRecordProtection.advanceare unpinned (they are reached throughTlsTcpServer.close); late-Finished resumption is a defect (documented behaviour).#653, reviewed and posted at
ba849c5. The worker is pushing a merge-main commit,dd5117b.sentClose=falseafter a gap of about 10 PTO.resetis never driven, andqwConnZerohas gaps.Rejected ledger additions (do not raise again):
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
amritk commentedon Oct 16, 2026
Log, 2026-10-16 ~10:05Z: review rounds 2 and 3
#653 (quic-holders)
43dc940). All round-1 items are fixed. The closing period now starts intakeCloseInto. 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.7b9f3b1, which I diffed: 3 lines across 2 files.7b9f3b1). No code defects. 3 wording items confirmed:tests/wipes/quic.jsnames a non-existent wipe on the failure path.#652 (tls-holders)
2862f8d). Round 1 is fixed and the code has no defect. Every wipe line I deleted moved an -O2 count.handleFinished, whose wipe is pinned only by its -O2 count;restartis conflated withstart;tlsNextTrafficSecretis missing from TLS-1;ab9099eanchor.secureZero(next)being unpinned (deleting it fails 2 read-backs).Additions to the rejected ledger
QW_APP_WRITE_SLOTSis duplicated.resetuses zero entropy.sent === 1.std/README.md's quic-packet row: owned by stages 4 and 5.Generated by Claude Code
amritk commentedon Oct 16, 2026
Log, 2026-10-16 ~10:40Z: stage 2 (tls-holders) merged as #652 →
b322a4bMerge gate:
54b2512; I verified the diff myself.326440e, including quic-interop-runner (ngtcp2 and quic-go), curl HTTP/3, Chrome WebTransport and h2spec.mainat464f41eis in the head.net_tls_memoryline and the generated goldens.Records:
docs/security/README.mdTotal is now 81 fixed and 19 open Lows.Next steps:
main(now including fix(tls): wipe every key a TLS connection holds when it ends #652) and takes the routed fix:wipeAllcallstls.wipeResumption(). After CI, it goes through the merge gate.Generated by Claude Code
amritk commentedon Oct 16, 2026
Log, 2026-10-16 ~11:23Z: stage 3 (quic-holders) merged as #653 →
e21e509Merge gate:
wipeAll→tls.wipeResumption(). I verified that change and its count notes myself.src/, so the goldens were regenerated each time.91af7f2, including interop.mainat3756681included.Records:
Running now:
session_018gySd6itL5j4UPeVuAAjXa): the TLS half is done and pushed. It is merging main for the QUIC half.session_01URstA1Mewe8kiMxysyqKRo): spawned. Its PR carriesFixes #430.The quic-holders worker is archived.
Generated by Claude Code
amritk commentedon Oct 16, 2026
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)tlsNextTrafficSecretedit inrecord.ts, which was a forced caller change.tests/wipesarea files.net_quic_wipes/expected.out: 106 lines stay byte-identical and the edit adds 12 lines plus the tally. No fix(quic): wipe a connection's packet keys and AES schedules when a level, generation or connection ends #653 check is weakened.quicKeysUpdateshares hp with its input. The preferred fix is to copy it.tlsMasterSecretIntowipes the master secret.Stage 5, quic-server-keys @
36b0c73release()/stop()in the reply buffers. The preferred fix is to wipe them.quicStatelessResetToken.stop()zeroes a shared config in place. Document one config per server, since other users would otherwise forge under a zero key.release(), onlyhandleis refused.http3.mdsentence edit is approved.Rejected:
qwOneShotearly-return leak.Both workers have been told to make one push, open the PR, and not merge. Deadline 14:30Z.
Generated by Claude Code
amritk commentedon Oct 16, 2026
Log, 2026-10-16 ~13:56Z: stage 5 (quic-server-keys) merged as #664 →
33bd848Merge gate:
abc4458(no findings).6729c8eadds one std/README line and a merge of main. I verified the net diff against the reviewed change: hunk offsets only.no attributionfailure was superseded by two later passes after the footer was stripped.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.Records:
Http3Server.stop/QuicListener.releaseare 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):
e37b82d. Round 2 verified 8 of 9 round-1 fixes; the last two small items were fixed and I diffed the delta myself.The quic-server-keys worker is archived.
Generated by Claude Code
amritk commentedon Oct 16, 2026
Log, 2026-10-16 ~14:17Z: stage 4 (secret-boundary) merged as #665 →
35d3771. All five stages are onmain.Merge gate:
e37b82d. Round 2 verified 8 of 9 round-1 fixes, and the last two small items were fixed; I diffed that change myself.be20411only merges main with fix(quic): wipe the stateless-reset and Retry token keys and the listener seed when the server is released #664 in.feat(crypto)!). The PR body saysPart of #430.ab9099eb322a4be21e50933bd84835d3771Open for the owner:
QuicKeysthat a caller keeps or drops withoutquicKeysWipe.Secretmay not be a field (NL2430), and lifting that rule was out of scope.Acceptance (phase 7) is running on merged main. All worker sessions are archived.
Generated by Claude Code
amritk commentedon Oct 16, 2026
Acceptance (phase 7) on merged
main@35d3771Secretcannot hold (NL2430): an open connection's field-held secrets, and aQuicKeysthe 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.node tests/run.js survive25/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._f64twin, and all pass.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.u8[]to a migrated derivation is a compile errorreject_secret_tls_plain_keyandreject_secret_quic_plain_keypass (reject_secret95/0).aesKeyWipewhen TLS and QUIC wipe a key. The vector registers and their spills are recorded as unreached.npm test, wasi and performance green on mainnpm test19,464 passed, 0 failed, 1 skipped (the container lacks aarch64crt1.o), with no DEGRADED banner.Owner decision pending: whether TLS-1 and QUIC-1 "Open, narrowed" satisfies #430. Their remainders exist only because a
Secretmay 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
amritk commentedon Oct 16, 2026
Close-out: owner decision recorded. #430 stays open. Follow-up #668, which lifts NL2430 so a
Secretcan 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