14 KiB
14 KiB
Tasks — Restore the keychain + packer top-layer; rename crypto → crypto-core
Every checkbox MUST reflect work actually done in this change. Do not retroactively tick boxes for work that was already in main when this change opened. Do not tick a box for a test that doesn't exist or doesn't pin the requirement it claims to verify. Retroactive ticking is the failure mode this template guards against.
1. Spec authoring
- 1.1 Read
../suaveplan/enterprise/keys.md,server-keys.md,crypto.md,packer.md,packer-browser.mdend-to-end - 1.2 Read existing
openspec/specs/crypto/spec.md(current primitive-layer spec; will be moved undercrypto-core) - 1.3 Read
openspec/AGENTS.mdSections 13 and 14 - 1.4 Draft
proposal.mdfromopenspec/templates/proposal.md— DONE in this change folder - 1.5 Fill in
## Source mappingtable — every requirement cites a line range OR appears in the "no source line" table with justification — DONE - 1.6 Draft spec deltas:
- 1.6.a
specs/crypto-core/spec.md— RENAMED + import-allowlist requirement - 1.6.b
specs/crypto/spec.md— full new spec (Keychain, Vault, Packer, ChunkedPacker, Authority, RevocationList, AuditLog, Identity hierarchy, domain tags, KAT vectors) - 1.6.c
specs/crypto-cli/spec.md— eight commands + UX rules + exit codes - 1.6.d
specs/crypto-keys/spec.md— REMOVED with migration pointer - 1.6.e
specs/crypto-vault/spec.md— REMOVED with migration pointer - 1.6.f
specs/slsa/spec.md— MODIFIED: builder identity viaAuthority
- 1.6.a
- 1.7 Every requirement in every spec delta carries a
**Verified by:**line naming concreteit(...)titles
2. Implementation — @suaveplan/bitpacker (bytes field)
- 2.0.1 Extend
FieldTypewith"bytes"and addbyteLengthtoFieldDefZod schema (packages/primitives/bitpacker/src/types/common.ts) - 2.0.2 Implement bytes encoding in
BitWriter: pad to byte boundary, writebyteLengthbytes, pad after; matching path inBitReader - 2.0.3 Expose
BitWriter.byteAlign()andBitReader.byteAlign()as public methods - 2.0.4 Update
BitSchemasize computation for variable-length payloads; document fixed-vs-runtime size semantics - 2.0.5 Add error codes:
SCHEMA.INVALID_BYTE_LENGTH,PACK.BYTES_LENGTH_MISMATCH,UNPACK.INSUFFICIENT_BYTES - 2.0.6 KAT-assert that pre-change schemas (uint/int/bool/fixed only) produce byte-identical output (
tests/legacy-schema-kat.test.ts) - 2.0.7 Sibling
.mdfor the new field type plus updatedbit-schema.md - 2.0.8 Pre-publish gate green for
@suaveplan/bitpacker
2. Implementation — @suaveplan/crypto-core (rename)
- 2.1 Create
packages/crypto/crypto-core/and move every file frompackages/crypto/crypto/(preserve git history viagit mv) - 2.2 Update
package.jsonname→@suaveplan/crypto-core - 2.3 Update every
workspace:^reference in the monorepo from@suaveplan/cryptoto@suaveplan/crypto-coreexcept for consumers that should now use the new opinionated@suaveplan/crypto - 2.4 Update every
importstatement accordingly (codemod:bun scripts/codemods/rename-crypto-to-core.ts) - 2.5 Update
CLAUDE.mdpackage list entry for crypto → crypto-core - 2.6 Verify primitive surface is byte-identical:
bunx turbo build --filter=@suaveplan/crypto-coreand diffdist/index.d.tsagainst pre-rename snapshot
3. Implementation — @suaveplan/crypto (new opinionated layer)
- 3.1 Scaffold
packages/crypto/crypto/from the package template (package.json,tsconfig.lib.json,vite.config.ts,vitest.config.ts,README.md,src/index.ts) - 3.2 Set
package.jsondeps:crypto-core,crypto-utils,crypto-hash,crypto-curve,crypto-cipher,bitpacker,error,events,types,i18n,opentelemetry,@scure/bip39,zod - 3.3 Move
crypto-keys/src/keypair/*→crypto/src/keychain/keypair/* - 3.4 Move
crypto-keys/src/keychain/*→crypto/src/keychain/*(memory + serialized adapters) - 3.5 Move
crypto-vault/src/mnemonic/*→crypto/src/vault/mnemonic/* - 3.6 Move
crypto-vault/src/vault/*→crypto/src/vault/* - 3.7 Implement
DerivationCoordinate(purpose ASCII[4] ‖ application ASCII[4] ‖ layoutId u8 ‖ <layout fields>) andDerivationLayoutregistry; shipDERIVATION_LAYOUT_V1(u16 + u32 + u8 + u8 = 64 bits exactly) - 3.8 Implement open
purposeregistry seeded with the eight defaults (mast/encr/sign/kx/salt/hash/strm/dev);registerPurpose(tag)adds without wire-format break - 3.9 Implement 8-slot
Keychaintype usingDerivationCoordinateper slot, plus{algorithm, notBefore, notAfter, secretKey?, publicKey?} - 3.10 Implement and commit wire-format schemas (
DERIVATION_COORDINATE_SCHEMA,KEY_SLOT_PRIVATE_SCHEMA,KEY_SLOT_PUBLIC_SCHEMA,VAULT_PAYLOAD_SCHEMA,PUBLIC_BUNDLE_SCHEMA,CHAIN_BUNDLE_SCHEMA,CRL_ENTRY_SCHEMA,AUDIT_ENTRY_SCHEMA); export each fromsrc/wire-format/; commit golden-bytes fixtures undertest-fixtures/wire-format/ - 3.11 Implement
packPublicBundle/unpackPublicBundle(parent-signed underDOMAIN_PUBLIC_KEYCHAIN_V1; embeddedissuerRefpart of signed bytes) - 3.12 Implement
packChainBundle/parseChainBundle/verifyChain(bundle, trustAnchor, clock, crl)walker (leaf-first concatenation; per-link validity-window, CRL, issuer-match, signature checks; max 16 links) - 3.13 Implement
Authorityinterface and in-memory backend (InMemoryAuthority) - 3.14 Implement
Packer/Unpacker(encrypt-then-sign envelope; domain-tag prefix; replay-window check) - 3.15 Implement
ChunkedPacker/ChunkedUnpacker(per-chunk signature; reassembly with tamper detection) - 3.16 Implement
Vaultwith three encryption regimes: (a) public bundle (plaintext, parent-signed); (b) private-key blob (password-AEAD, rotatable viachangePasswordwithout re-derivation); (c) vault payload (auth-box-sealed under keypair, parent-signed underDOMAIN_VAULT_V1, carriesmaster/seedCount/keychain/data: Uint8Array); BIP39 + Argon2id;lock()zeroises;rotateApplicationData()re-seals payload only - 3.17 Implement
RevocationList(root-signed CRL; append-only; verify-against-anchor) - 3.18 Implement
AuditLog(append-only Merkle log; root-signed root; tamper-detection on past entries) - 3.19 Implement
RootIdentity,ServerIdentity,createRoot,createServer,rollServer,revokeServer(replacingChainSigner) - 3.20 Implement domain-separation tag constants as branded
stringtypes; signature paths require a tag - 3.21 Wire
@suaveplan/errorcodes (one per failure class — see spec for full list) - 3.22 Wire OTel spans + counters + histograms via
@suaveplan/opentelemetry; redaction-test asserts no key bytes in attributes - 3.23 Wire
@suaveplan/eventsfor pack/unpack/chunk/reassemble/issue/roll/revoke events - 3.24 Inject
ClockandRng(from@suaveplan/types) at every entry point; noDate.now()/Math.random()anywhere insrc/ - 3.25 Wire
@suaveplan/i18nfor all user-visible error messages - 3.26 Co-located
.mdper implementation file (≥ 200 words, basic + advanced examples)
4. Implementation — @suaveplan/crypto-cli (new)
- 4.1 Scaffold
packages/crypto/crypto-cli/withpackage.jsonbinentry →dist/keychain.js - 4.2 Deps:
@suaveplan/cli,@suaveplan/readline,@suaveplan/crypto,@suaveplan/error,@suaveplan/types,@suaveplan/i18n,@suaveplan/opentelemetry - 4.3 Implement
keychain root init— generate root identity, write encrypted vault, emit public bundle - 4.4 Implement
keychain server init— generate server identity under a root, emit signed public bundle, append to audit log - 4.5 Implement
keychain server roll— version-bump rotation, signed by root, append CRL entry for prior version, append audit-log entry - 4.6 Implement
keychain server revoke— append CRL entry, append audit-log entry, do not re-issue - 4.7 Implement
keychain inspect— view a packed public keychain (verifies self-signature) - 4.8 Implement
keychain verify— verify any signed envelope against a trust anchor + CRL - 4.9 Implement
keychain export-public— strip secrets, output portable public bundle - 4.10 Implement
keychain restore— re-derive a vault from BIP39 mnemonic + version - 4.11 TTY-only password input via
cli'spasswordprompt;--passwordflag rejected with a security-pointer error - 4.12 Stdin/stdout streamable for every command;
--output json|binary|text - 4.13 Distinct exit codes: 0 success, 2 usage error, 10 bad password, 11 tampered vault, 12 expired key, 13 revoked key, 14 missing trust anchor, 15 audit-log mismatch
- 4.14
--idempotency-keyonserver rollandserver revoke; local-file ledger - 4.15 OTel spans + structured logger output for every command
5. Implementation — @suaveplan/slsa (modified)
- 5.1 Replace direct
crypto.ed25519.sign(secretKey, …)calls withAuthority.sign(DOMAIN_SLSA_BUILDER_V1, …) - 5.2 Builder identity now constructed from a
ServerIdentityissued under a SLSA-builder root - 5.3 Provenance test fixtures regenerated with new construction path; DSSE envelope output unchanged (asserted by round-trip equality test)
- 5.4
@suaveplan/cryptoadded as workspace dep;@suaveplan/crypto(nowcrypto-core) for signing removed - 5.5
@suaveplan/crypto-hashand@suaveplan/crypto-utilsretained — hashing + encoding helpers stay open - 5.6 New test
tests/no-direct-crypto-core.test.tspins import discipline
6. Anti-sediment enforcement
- 6.1 Add
scripts/conformance/crypto-core-allowlist.ymlwith two entries:@suaveplan/crypto,@suaveplan/crypto-cli - 6.2 Extend
scripts/openspec-conformance.tsto fail witherrorseverity when any package outside the allowlist imports cryptographic primitives fromcrypto-core/crypto-hash-as-MAC /crypto-curve/crypto-cipher(encoding helpers fromcrypto-utilsand pure-hash uses fromcrypto-hashremain open) - 6.3 Test
scripts/conformance/crypto-core-allowlist.test.tsexercises positive (allowed) and negative (forbidden) cases against fixture packages - 6.4 Wire-format KAT test at
packages/crypto/crypto/tests/wire-format-kat.test.tsasserts golden bytes for: vault-v1, public-keychain-v1, packer-envelope-v1, chunk-v1, crl-v1, audit-entry-v1 - 6.5 Property-based round-trip test at
packages/crypto/crypto/tests/property-roundtrips.test.ts(fast-check)
7. Tests
- 7.1 Unit tests at
src/<feature>/<feature>.test.tscovering every scenario in the spec deltas - 7.2 Spec-conformance tests at
src/<feature>/<feature>.spec-conformance.test.tspinning type-level / observability / strict-mode / lifecycle scenarios - 7.3 Integration / contract / property tests where the spec demands them
- 7.4 Every
it(...)carries{ timeout: 5000 }(or 30000 for genuinely async) - 7.5 Tampered-vault, tampered-chunk, expired-key, revoked-key scenarios each pinned by a named
it(...) - 7.6 Replay-rejection test (old envelope, fresh verify) pinned
8. Documentation
- 8.1 Sibling
.mdfor every implementation.ts(≥200 words, basic + advanced examples, full API reference) - 8.2 README at
packages/crypto/crypto/README.mdlinks every co-located.md - 8.3 README at
packages/crypto/crypto-cli/README.mdlists every command with examples - 8.4 Update
CLAUDE.mdpackage listing: removecrypto-keys+crypto-vault, renamecrypto→crypto-core, addcrypto+crypto-cli - 8.5 Refresh
MANIFEST.mdviabun scripts/folder-reorg/generate-manifest.ts
9. Pre-archive verification (the 5-gate triple, per package touched)
For each of @suaveplan/crypto-core, @suaveplan/crypto, @suaveplan/crypto-cli, @suaveplan/slsa:
- 9.1
bun run check— Biome 0 errors / 0 warnings / 0 info across the whole monorepo - 9.2
bunx turbo lint typecheck test --filter=@suaveplan/<pkg>— green - 9.3
bunx turbo test:coverage --filter=@suaveplan/<pkg>— 100/100/100/100 - 9.4
bun scripts/openspec-conformance.ts @suaveplan/<pkg>— exit 0, noerrorfindings - 9.5
bunx turbo build --filter=@suaveplan/<pkg>— green
10. Pre-archive 7-row checklist (AGENTS.md Section 13)
- 10.1 Generics threaded;
expectTypeOfassertions on every literal-type claim (slot map, algorithm tag, domain-separation tag) - 10.2 Zod schemas wired at every declared boundary (Keychain, Vault, packed envelopes, CRL entries, audit entries, CLI command args)
- 10.3 Every error code in
error-codes.tsthrown by at least one production path - 10.4 Strict-mode behaviours tested both on and off (algorithm allowlist, validity-window enforcement, replay-window enforcement)
- 10.5 OTel instruments emitted from production paths and asserted with a real in-memory
MeterProvider - 10.6 Lifecycle dedup tested where claimed (replay-window dedup of nonces; idempotency-key dedup in CLI)
- 10.7 No retroactive tick — every box above reflects work done in this change
11. Archive
- 11.1 Fold spec deltas into
openspec/specs/crypto-core/spec.md,openspec/specs/crypto/spec.md,openspec/specs/crypto-cli/spec.md,openspec/specs/slsa/spec.md - 11.2 Delete
openspec/specs/crypto-keys/andopenspec/specs/crypto-vault/(folded packages) - 11.3 Move
openspec/changes/restore-crypto-keychain-packer/→openspec/archive/<yyyy-mm-dd>-restore-crypto-keychain-packer/ - 11.4
MANIFEST.mdrefreshed and committed - 11.5 Root
README.mdupdated to reflect new crypto package layout
Audit Note (2026-05-02)
DEFERRED in the genesis spec-cleanup pass on 2026-05-02. Rationale:
- 101 tasks across multiple packages: rename @suaveplan/crypto → @suaveplan/crypto-core, introduce new @suaveplan/crypto top-layer (Packer/Unpacker/Authority/AuditLog/RevocationList/Keychain/Vault), modify @suaveplan/slsa, modify @suaveplan/bitpacker, and add new @suaveplan/crypto-cli package. Genuinely large architectural change requiring sustained focused work — not safe to bulk-approve under an audit pass. 0/101 done.