Files
2026-05-27 23:29:53 +01:00

109 lines
9.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tasks — crypto-keys
> Every checkbox MUST reflect work actually done in this change.
## 1. Spec authoring
- [x] 1.1 Read `openspec/AGENTS.md` §0, §0a, §0b, §0c, §6a, §7, §13, §14 end-to-end
- [x] 1.2 Read `../suaveplan/packages/universal/keys/src/lib/chain-signer.ts` in full
- [x] 1.3 Read `../suaveplan/packages/universal/keys/src/lib/keys-common.ts` (factory section)
- [x] 1.4 Read `../suaveplan/enterprise/keys.md` to confirm scope boundary
- [x] 1.5 Read genesis `packages/crypto/crypto/src/keychain/keychain.ts` and `keychain/index.ts` (public surface)
- [x] 1.6 Skim every `packages/crypto/crypto-curve/src/primitives/*/impl/*.noble/*.ts` and confirm sign/verify signature
- [x] 1.7 Draft `proposal.md` with scope, divergence table, source mapping, affected dependents
- [x] 1.8 Draft `design.md` covering file layout, three codecs, three hashers, algorithm dispatch, error-code map, factory layer
- [x] 1.9 Draft `specs/crypto-keys/spec.md` with `### Requirement: / #### Scenario: / **Verified by:**` form
- [x] 1.10 Run `openspec validate add-crypto-keys` and fix any structural failures
## 2. Scaffolding
- [x] 2.1 Create `packages/crypto/crypto-keys/` mirroring `packages/database/seed/` layout
- [x] 2.2 Write `package.json` v0.0.0, `sideEffects: false`, subpath exports for every feature module (error-codes/error-codes, types/{chain-level,hasher,codec}, hashers/{raw,domain,merkle}/<name>, codecs/{tlv,recursive-ed25519,cbor}/<name>, signer/signer, factory/factory)
- [x] 2.3 Wire deps: `@suaveplan/crypto: workspace:^`, `@suaveplan/crypto-curve: workspace:^`, `@suaveplan/crypto-hash: workspace:^`, `@suaveplan/cbor: workspace:^`, `@suaveplan/error: workspace:^`, `@suaveplan/types: workspace:^`, `tslib: catalog:build`
- [x] 2.4 Wire devDeps: `@suaveplan/testing: workspace:^`, `@types/node: catalog:`, `@vitest/coverage-istanbul: catalog:testing`, `fast-check: catalog:testing`, `typescript: catalog:`, `vite: catalog:build`, `vite-plugin-dts: catalog:build`, `vitest: catalog:testing`
- [x] 2.5 Write `tsconfig.json`, `tsconfig.lib.json` extending the genesis base
- [x] 2.6 Write `vite.config.ts` (multi-entry, ES-only, preserveModules)
- [x] 2.7 Write `vitest.config.ts` using a project-local config with `pool: 'forks'` + 100% Istanbul thresholds
- [x] 2.8 Write `tests/setup/polyfills.ts` (universal tier, no polyfills needed — same pattern as seed)
- [x] 2.9 Write `README.md` (≥ 200 words, all 6 required sections per CLAUDE.md docs gate)
## 3. Implementation — errors & shared types
- [x] 3.1 `src/error-codes/error-codes.ts` — `defineCodes("CHAIN_KEYS", [...])` with 7 codes; `ChainKeysError extends SuaveplanError<ChainKeysErrorCode>` with `depth?` / `algorithm?` discriminants
- [x] 3.2 `src/error-codes/error-codes.test.ts` — every code reachable from a `ChainKeysError` construction; `cause` chain preservation; discriminants populate
- [x] 3.3 `src/error-codes/error-codes.md` — full doc with required sections
- [x] 3.4 `src/error-codes/index.ts` — explicit named re-exports
- [x] 3.5 `src/types/chain-level.ts` — `ChainLevel`, `VerificationResult`, `ParsedChainLevel` interfaces; `chain-level.md` sibling
- [x] 3.6 `src/types/hasher.ts` — `ChainHasher` interface (`hash`, `name`); `hasher.md` sibling
- [x] 3.7 `src/types/codec.ts` — `ChainCodec` interface (`encode`, `decode`, `name`); `codec.md` sibling
## 4. Implementation — hashers (3)
- [x] 4.1 `src/hashers/raw/raw.ts` — `RawHasher implements ChainHasher` (identity hash)
- [x] 4.2 `src/hashers/raw/raw.test.ts` — identity property; ignores parent; name is "raw"
- [x] 4.3 `src/hashers/raw/raw.md` + `index.ts`
- [x] 4.4 `src/hashers/domain/domain.ts` — `DomainHasher implements ChainHasher` with constructor-supplied `domainTag` (1–64 ASCII bytes); raises `CHAIN_KEYS.INVALID_DOMAIN_TAG`
- [x] 4.5 `src/hashers/domain/domain.test.ts` — `sha256(utf8(tag) || encodedKeys)`; constructor validation (empty / oversize); ignores parent; name is "domain"
- [x] 4.6 `src/hashers/domain/domain.md` + `index.ts`
- [x] 4.7 `src/hashers/merkle/merkle.ts` — `MerkleHasher implements ChainHasher`; root hash without parent, parent root hash for non-root
- [x] 4.8 `src/hashers/merkle/merkle.test.ts` — root path; non-root commits to parent; tampering with parent invalidates child
- [x] 4.9 `src/hashers/merkle/merkle.md` + `index.ts`
## 5. Implementation — codecs (3)
- [x] 5.1 `src/codecs/tlv/tlv.ts` — `TlvChainCodec implements ChainCodec`; `[version=1][hasher][per level: algorithm + keys_len:u16 + keys + sig_len:u16 + sig]`; algorithm-id ↔ enum byte table; hasher-name ↔ enum byte table; raises `CHAIN_KEYS.INVALID_FORMAT` on bad version/truncation/length overrun
- [x] 5.2 `src/codecs/tlv/tlv.test.ts` — encode/decode round-trip for every algorithm; truncated buffer; bad version byte; length overrun; multi-level chain
- [x] 5.3 `src/codecs/tlv/tlv.md` + `index.ts`
- [x] 5.4 `src/codecs/recursive-ed25519/recursive-ed25519.ts` — `RecursiveEd25519Codec implements ChainCodec`; fixed `keys_size = 8 * KEY_SLOT_PUBLIC_BYTE_SIZE`; rejects non-Ed25519 at encode (`CHAIN_KEYS.UNSUPPORTED_ALGORITHM`)
- [x] 5.5 `src/codecs/recursive-ed25519/recursive-ed25519.test.ts` — Ed25519 round-trip; non-Ed25519 rejection; multi-level recursion; bad version
- [x] 5.6 `src/codecs/recursive-ed25519/recursive-ed25519.md` + `index.ts`
- [x] 5.7 `src/codecs/cbor/cbor.ts` — `CborChainCodec implements ChainCodec`; CBOR map `{ v, h, levels: [{ a, k, s }] }`; deterministic encoder; rejects unknown `h`/`a` strings
- [x] 5.8 `src/codecs/cbor/cbor.test.ts` — encode/decode round-trip; deterministic byte equality; bad CBOR; missing fields
- [x] 5.9 `src/codecs/cbor/cbor.md` + `index.ts`
## 6. Implementation — ChainedKeySigner
- [x] 6.1 `src/signer/signer.types.ts` — `ISigner` / `SignerOptions` / `SignerSignOptions` interfaces
- [x] 6.2 `src/signer/signer.ts` — `ChainedKeySigner` class:
- constructor `{ hasher?, codec? }` defaults `RawHasher` + `TlvChainCodec`
- `sign(publicKeys, signSlot, parent?)` — encodes public keychain, computes bytes-to-sign via hasher (passing parsed parent's leaf level if applicable), dispatches to `@suaveplan/crypto-curve` sign by `signSlot.algorithm`, prepends new level via codec
- `verify(chainBytes, trustedRoot)` — decodes via codec, walks every level computing the same bytes-to-sign, dispatches to `crypto-curve` verify, checks deepest signer matches `trustedRoot.public` via constant-time compare, returns `{ valid, depth, levels, errors }`
- `parse(chainBytes)` — decode-only
- `stripToDepth(chainBytes, depth)` — re-encode the leaf-most `depth` levels via codec
- Wraps any crypto-curve exception as `ChainKeysError` with `cause` preserved
- [x] 6.3 `src/signer/signer.test.ts` — every public method × every (hasher, codec) combination × every algorithm; tampering tests at every byte boundary; abort signal future-proofing (omitted intentionally; sign is in-memory)
- [x] 6.4 `src/signer/signer.md` + `index.ts`
## 7. Implementation — hierarchical factory
- [x] 7.1 `src/factory/factory.ts` — `createRootKeysWithChain`, `createServerKeysWithChain`, `rollServerKeysWithChain`; all default to `new ChainedKeySigner()` when omitted; raise `CHAIN_KEYS.INVALID_KEYCHAIN` when `rootKeys.master`/`rootKeys.sign` lacks private material
- [x] 7.2 `src/factory/factory.test.ts` — 3-level chain (root → server → user); verify against root; tamper at server level → fails; missing root.master / root.sign → INVALID_KEYCHAIN; injected signer with non-default hasher works end-to-end
- [x] 7.3 `src/factory/factory.md` + `index.ts`
## 8. Root barrel & public surface
- [x] 8.1 `src/index.ts` — re-export every public symbol via explicit named exports (no `export *` — rule §6a)
- [x] 8.2 Verify `bunx turbo build --filter=@suaveplan/crypto-keys` succeeds and dist contains every advertised subpath
## 9. Pre-archive gates
- [x] 9.1 `bunx turbo lint --filter=@suaveplan/crypto-keys` green; zero biome suppressions
- [x] 9.2 `bunx turbo typecheck --filter=@suaveplan/crypto-keys` green
- [x] 9.3 `bunx turbo test --filter=@suaveplan/crypto-keys` green; zero `.skip` / `.only` / `it.todo`
- [x] 9.4 `bunx turbo test:coverage --filter=@suaveplan/crypto-keys` green at 100% statement/branch/function/line (Istanbul)
- [x] 9.5 `bun run docs:check --filter=@suaveplan/crypto-keys` green
- [x] 9.6 `grep -rnE "from ['\"](vitest|bun:test)['\"]" packages/crypto/crypto-keys/src/` → zero matches (rule §7)
- [x] 9.7 `grep -rnE "^export \*" packages/crypto/crypto-keys/src/` → only the root barrel and exempt forms (rule §6a)
- [x] 9.8 `grep -rnE "from ['\"](node:|bun:|fs|path|stream|net|http)['\"]" packages/crypto/crypto-keys/src/` → zero matches (universal tier)
- [x] 9.9 `grep -rn "@deprecated\|TODO\|FIXME\|ts-ignore\|ts-expect-error\|\\.skip\|\\.only\|\\bany\\b\|console\\.log" packages/crypto/crypto-keys/src/ --include="*.ts"` → zero matches
- [x] 9.10 Every `### Requirement` in `specs/crypto-keys/spec.md` checked off against the implementation
- [x] 9.11 `bun scripts/folder-reorg/generate-manifest.ts`; `MANIFEST.md` includes `@suaveplan/crypto-keys`
- [x] 9.12 `STATUS.md` row added or updated — entry under crypto plugin, status `✅`
## 10. Fold + archive
- [x] 10.1 Fold `specs/crypto-keys/spec.md` into `openspec/specs/crypto-keys/spec.md` (new canonical capability; convert `## ADDED Requirements` → `## Requirements` and prepend `## Purpose`)
- [x] 10.2 `openspec validate crypto-keys` clean
- [x] 10.3 Move `openspec/changes/add-crypto-keys/` → `openspec/archive/$(date +%Y-%m-%d)-add-crypto-keys/`
- [x] 10.4 Commit per CLAUDE.md commit-style; do not push without user approval