9.5 KiB
9.5 KiB
Tasks — crypto-keys
Every checkbox MUST reflect work actually done in this change.
1. Spec authoring
- 1.1 Read
openspec/AGENTS.md§0, §0a, §0b, §0c, §6a, §7, §13, §14 end-to-end - 1.2 Read
../suaveplan/packages/universal/keys/src/lib/chain-signer.tsin full - 1.3 Read
../suaveplan/packages/universal/keys/src/lib/keys-common.ts(factory section) - 1.4 Read
../suaveplan/enterprise/keys.mdto confirm scope boundary - 1.5 Read genesis
packages/crypto/crypto/src/keychain/keychain.tsandkeychain/index.ts(public surface) - 1.6 Skim every
packages/crypto/crypto-curve/src/primitives/*/impl/*.noble/*.tsand confirm sign/verify signature - 1.7 Draft
proposal.mdwith scope, divergence table, source mapping, affected dependents - 1.8 Draft
design.mdcovering file layout, three codecs, three hashers, algorithm dispatch, error-code map, factory layer - 1.9 Draft
specs/crypto-keys/spec.mdwith### Requirement: / #### Scenario: / **Verified by:**form - 1.10 Run
openspec validate add-crypto-keysand fix any structural failures
2. Scaffolding
- 2.1 Create
packages/crypto/crypto-keys/mirroringpackages/database/seed/layout - 2.2 Write
package.jsonv0.0.0,sideEffects: false, subpath exports for every feature module (error-codes/error-codes, types/{chain-level,hasher,codec}, hashers/{raw,domain,merkle}/, codecs/{tlv,recursive-ed25519,cbor}/, signer/signer, factory/factory) - 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 - 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 - 2.5 Write
tsconfig.json,tsconfig.lib.jsonextending the genesis base - 2.6 Write
vite.config.ts(multi-entry, ES-only, preserveModules) - 2.7 Write
vitest.config.tsusing a project-local config withpool: 'forks'+ 100% Istanbul thresholds - 2.8 Write
tests/setup/polyfills.ts(universal tier, no polyfills needed — same pattern as seed) - 2.9 Write
README.md(≥ 200 words, all 6 required sections per CLAUDE.md docs gate)
3. Implementation — errors & shared types
- 3.1
src/error-codes/error-codes.ts—defineCodes("CHAIN_KEYS", [...])with 7 codes;ChainKeysError extends SuaveplanError<ChainKeysErrorCode>withdepth?/algorithm?discriminants - 3.2
src/error-codes/error-codes.test.ts— every code reachable from aChainKeysErrorconstruction;causechain preservation; discriminants populate - 3.3
src/error-codes/error-codes.md— full doc with required sections - 3.4
src/error-codes/index.ts— explicit named re-exports - 3.5
src/types/chain-level.ts—ChainLevel,VerificationResult,ParsedChainLevelinterfaces;chain-level.mdsibling - 3.6
src/types/hasher.ts—ChainHasherinterface (hash,name);hasher.mdsibling - 3.7
src/types/codec.ts—ChainCodecinterface (encode,decode,name);codec.mdsibling
4. Implementation — hashers (3)
- 4.1
src/hashers/raw/raw.ts—RawHasher implements ChainHasher(identity hash) - 4.2
src/hashers/raw/raw.test.ts— identity property; ignores parent; name is "raw" - 4.3
src/hashers/raw/raw.md+index.ts - 4.4
src/hashers/domain/domain.ts—DomainHasher implements ChainHasherwith constructor-supplieddomainTag(1–64 ASCII bytes); raisesCHAIN_KEYS.INVALID_DOMAIN_TAG - 4.5
src/hashers/domain/domain.test.ts—sha256(utf8(tag) || encodedKeys); constructor validation (empty / oversize); ignores parent; name is "domain" - 4.6
src/hashers/domain/domain.md+index.ts - 4.7
src/hashers/merkle/merkle.ts—MerkleHasher implements ChainHasher; root hash without parent, parent root hash for non-root - 4.8
src/hashers/merkle/merkle.test.ts— root path; non-root commits to parent; tampering with parent invalidates child - 4.9
src/hashers/merkle/merkle.md+index.ts
5. Implementation — codecs (3)
- 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; raisesCHAIN_KEYS.INVALID_FORMATon bad version/truncation/length overrun - 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 - 5.3
src/codecs/tlv/tlv.md+index.ts - 5.4
src/codecs/recursive-ed25519/recursive-ed25519.ts—RecursiveEd25519Codec implements ChainCodec; fixedkeys_size = 8 * KEY_SLOT_PUBLIC_BYTE_SIZE; rejects non-Ed25519 at encode (CHAIN_KEYS.UNSUPPORTED_ALGORITHM) - 5.5
src/codecs/recursive-ed25519/recursive-ed25519.test.ts— Ed25519 round-trip; non-Ed25519 rejection; multi-level recursion; bad version - 5.6
src/codecs/recursive-ed25519/recursive-ed25519.md+index.ts - 5.7
src/codecs/cbor/cbor.ts—CborChainCodec implements ChainCodec; CBOR map{ v, h, levels: [{ a, k, s }] }; deterministic encoder; rejects unknownh/astrings - 5.8
src/codecs/cbor/cbor.test.ts— encode/decode round-trip; deterministic byte equality; bad CBOR; missing fields - 5.9
src/codecs/cbor/cbor.md+index.ts
6. Implementation — ChainedKeySigner
- 6.1
src/signer/signer.types.ts—ISigner/SignerOptions/SignerSignOptionsinterfaces - 6.2
src/signer/signer.ts—ChainedKeySignerclass:- constructor
{ hasher?, codec? }defaultsRawHasher+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-curvesign bysignSlot.algorithm, prepends new level via codecverify(chainBytes, trustedRoot)— decodes via codec, walks every level computing the same bytes-to-sign, dispatches tocrypto-curveverify, checks deepest signer matchestrustedRoot.publicvia constant-time compare, returns{ valid, depth, levels, errors }parse(chainBytes)— decode-onlystripToDepth(chainBytes, depth)— re-encode the leaf-mostdepthlevels via codec- Wraps any crypto-curve exception as
ChainKeysErrorwithcausepreserved
- constructor
- 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) - 6.4
src/signer/signer.md+index.ts
7. Implementation — hierarchical factory
- 7.1
src/factory/factory.ts—createRootKeysWithChain,createServerKeysWithChain,rollServerKeysWithChain; all default tonew ChainedKeySigner()when omitted; raiseCHAIN_KEYS.INVALID_KEYCHAINwhenrootKeys.master/rootKeys.signlacks private material - 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 - 7.3
src/factory/factory.md+index.ts
8. Root barrel & public surface
- 8.1
src/index.ts— re-export every public symbol via explicit named exports (noexport *— rule §6a) - 8.2 Verify
bunx turbo build --filter=@suaveplan/crypto-keyssucceeds and dist contains every advertised subpath
9. Pre-archive gates
- 9.1
bunx turbo lint --filter=@suaveplan/crypto-keysgreen; zero biome suppressions - 9.2
bunx turbo typecheck --filter=@suaveplan/crypto-keysgreen - 9.3
bunx turbo test --filter=@suaveplan/crypto-keysgreen; zero.skip/.only/it.todo - 9.4
bunx turbo test:coverage --filter=@suaveplan/crypto-keysgreen at 100% statement/branch/function/line (Istanbul) - 9.5
bun run docs:check --filter=@suaveplan/crypto-keysgreen - 9.6
grep -rnE "from ['\"](vitest|bun:test)['\"]" packages/crypto/crypto-keys/src/→ zero matches (rule §7) - 9.7
grep -rnE "^export \*" packages/crypto/crypto-keys/src/→ only the root barrel and exempt forms (rule §6a) - 9.8
grep -rnE "from ['\"](node:|bun:|fs|path|stream|net|http)['\"]" packages/crypto/crypto-keys/src/→ zero matches (universal tier) - 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 - 9.10 Every
### Requirementinspecs/crypto-keys/spec.mdchecked off against the implementation - 9.11
bun scripts/folder-reorg/generate-manifest.ts;MANIFEST.mdincludes@suaveplan/crypto-keys - 9.12
STATUS.mdrow added or updated — entry under crypto plugin, status✅
10. Fold + archive
- 10.1 Fold
specs/crypto-keys/spec.mdintoopenspec/specs/crypto-keys/spec.md(new canonical capability; convert## ADDED Requirements→## Requirementsand prepend## Purpose) - 10.2
openspec validate crypto-keysclean - 10.3 Move
openspec/changes/add-crypto-keys/→openspec/archive/$(date +%Y-%m-%d)-add-crypto-keys/ - 10.4 Commit per CLAUDE.md commit-style; do not push without user approval