# 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.md` end-to-end - [ ] 1.2 Read existing `openspec/specs/crypto/spec.md` (current primitive-layer spec; will be moved under `crypto-core`) - [ ] 1.3 Read `openspec/AGENTS.md` Sections 13 and 14 - [ ] 1.4 Draft `proposal.md` from `openspec/templates/proposal.md` — DONE in this change folder - [ ] 1.5 Fill in `## Source mapping` table — 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 via `Authority` - [ ] 1.7 Every requirement in every spec delta carries a `**Verified by:**` line naming concrete `it(...)` titles ## 2. Implementation — `@suaveplan/bitpacker` (`bytes` field) - [ ] 2.0.1 Extend `FieldType` with `"bytes"` and add `byteLength` to `FieldDef` Zod schema (`packages/primitives/bitpacker/src/types/common.ts`) - [ ] 2.0.2 Implement bytes encoding in `BitWriter`: pad to byte boundary, write `byteLength` bytes, pad after; matching path in `BitReader` - [ ] 2.0.3 Expose `BitWriter.byteAlign()` and `BitReader.byteAlign()` as public methods - [ ] 2.0.4 Update `BitSchema` size 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 `.md` for the new field type plus updated `bit-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 from `packages/crypto/crypto/` (preserve git history via `git mv`) - [ ] 2.2 Update `package.json` `name` → `@suaveplan/crypto-core` - [ ] 2.3 Update every `workspace:^` reference in the monorepo from `@suaveplan/crypto` to `@suaveplan/crypto-core` *except* for consumers that should now use the new opinionated `@suaveplan/crypto` - [ ] 2.4 Update every `import` statement accordingly (codemod: `bun scripts/codemods/rename-crypto-to-core.ts`) - [ ] 2.5 Update `CLAUDE.md` package list entry for crypto → crypto-core - [ ] 2.6 Verify primitive surface is byte-identical: `bunx turbo build --filter=@suaveplan/crypto-core` and diff `dist/index.d.ts` against 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.json` deps: `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 ‖ `) and `DerivationLayout` registry; ship `DERIVATION_LAYOUT_V1` (u16 + u32 + u8 + u8 = 64 bits exactly) - [ ] 3.8 Implement open `purpose` registry 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 `Keychain` type using `DerivationCoordinate` per 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 from `src/wire-format/`; commit golden-bytes fixtures under `test-fixtures/wire-format/` - [ ] 3.11 Implement `packPublicBundle` / `unpackPublicBundle` (parent-signed under `DOMAIN_PUBLIC_KEYCHAIN_V1`; embedded `issuerRef` part 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 `Authority` interface 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 `Vault` with **three encryption regimes**: (a) public bundle (plaintext, parent-signed); (b) private-key blob (password-AEAD, rotatable via `changePassword` without re-derivation); (c) vault payload (auth-box-sealed under keypair, parent-signed under `DOMAIN_VAULT_V1`, carries `master`/`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` (replacing `ChainSigner`) - [ ] 3.20 Implement domain-separation tag constants as branded `string` types; signature paths require a tag - [ ] 3.21 Wire `@suaveplan/error` codes (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/events` for pack/unpack/chunk/reassemble/issue/roll/revoke events - [ ] 3.24 Inject `Clock` and `Rng` (from `@suaveplan/types`) at every entry point; no `Date.now()` / `Math.random()` anywhere in `src/` - [ ] 3.25 Wire `@suaveplan/i18n` for all user-visible error messages - [ ] 3.26 Co-located `.md` per implementation file (≥ 200 words, basic + advanced examples) ## 4. Implementation — `@suaveplan/crypto-cli` (new) - [ ] 4.1 Scaffold `packages/crypto/crypto-cli/` with `package.json` `bin` entry → `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`'s `password` prompt; `--password` flag 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-key` on `server roll` and `server 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 with `Authority.sign(DOMAIN_SLSA_BUILDER_V1, …)` - [ ] 5.2 Builder identity now constructed from a `ServerIdentity` issued 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/crypto` added as workspace dep; `@suaveplan/crypto` (now `crypto-core`) for signing removed - [ ] 5.5 `@suaveplan/crypto-hash` and `@suaveplan/crypto-utils` retained — hashing + encoding helpers stay open - [ ] 5.6 New test `tests/no-direct-crypto-core.test.ts` pins import discipline ## 6. Anti-sediment enforcement - [ ] 6.1 Add `scripts/conformance/crypto-core-allowlist.yml` with two entries: `@suaveplan/crypto`, `@suaveplan/crypto-cli` - [ ] 6.2 Extend `scripts/openspec-conformance.ts` to fail with `error` severity when any package outside the allowlist imports cryptographic primitives from `crypto-core` / `crypto-hash`-as-MAC / `crypto-curve` / `crypto-cipher` (encoding helpers from `crypto-utils` and pure-hash uses from `crypto-hash` remain open) - [ ] 6.3 Test `scripts/conformance/crypto-core-allowlist.test.ts` exercises positive (allowed) and negative (forbidden) cases against fixture packages - [ ] 6.4 Wire-format KAT test at `packages/crypto/crypto/tests/wire-format-kat.test.ts` asserts 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//.test.ts` covering every scenario in the spec deltas - [ ] 7.2 Spec-conformance tests at `src//.spec-conformance.test.ts` pinning 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 `.md` for every implementation `.ts` (≥200 words, basic + advanced examples, full API reference) - [ ] 8.2 README at `packages/crypto/crypto/README.md` links every co-located `.md` - [ ] 8.3 README at `packages/crypto/crypto-cli/README.md` lists every command with examples - [ ] 8.4 Update `CLAUDE.md` package listing: remove `crypto-keys` + `crypto-vault`, rename `crypto` → `crypto-core`, add `crypto` + `crypto-cli` - [ ] 8.5 Refresh `MANIFEST.md` via `bun 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/` — green - [ ] 9.3 `bunx turbo test:coverage --filter=@suaveplan/` — 100/100/100/100 - [ ] 9.4 `bun scripts/openspec-conformance.ts @suaveplan/` — exit 0, no `error` findings - [ ] 9.5 `bunx turbo build --filter=@suaveplan/` — green ## 10. Pre-archive 7-row checklist (AGENTS.md Section 13) - [ ] 10.1 Generics threaded; `expectTypeOf` assertions 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.ts` thrown 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/` and `openspec/specs/crypto-vault/` (folded packages) - [ ] 11.3 Move `openspec/changes/restore-crypto-keychain-packer/` → `openspec/archive/-restore-crypto-keychain-packer/` - [ ] 11.4 `MANIFEST.md` refreshed and committed - [ ] 11.5 Root `README.md` updated 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.