3.9 KiB
Why
The verifyChain(bundleBytes, trustAnchorBundleBytes, options) walker in packages/crypto/crypto/src/bundles/chain-bundle/chain-bundle.ts accepts a trust anchor as raw Uint8Array. There is no shipped helper to load that buffer from disk or environment, so every consumer (CLI, daemons, drive sync, network plugins) hand-rolls the same readFile + parsePublicBundle + self-verify sequence — or worse, skips the self-verify and trusts whatever bytes happen to live at the path. The umbrella change restore-crypto-keychain-packer-umbrella named this surface (loadTrustAnchor() / loadTrustAnchorFromEnv() / SUAVEPLAN_TRUST_ANCHOR_PATH) but the implementation slice was deferred. The crypto-spec-restore-versioning archive (2026-05-03) explicitly excluded the requirement because the src/trust-anchors/ directory does not exist on genesis. The chain-bundle source already references a TRUST_ANCHOR.MISMATCH error code (line 199 of chain-bundle.ts) that is never defined in errors/codes/codes.ts — a stale doc-only signal that the surface was planned and never built.
This change fills the gap with a small, dependency-free loader module that takes injectable filesystem and environment readers (no direct node:fs import in the universal package), self-verifies the bundle on load, and surfaces stable structured errors. The module is the single auditable point through which trust anchors enter a Suaveplan process — a security-relevant choke point.
What Changes
- MODIFIED
cryptocapability: add 1 requirement covering trust-anchor bootstrap. New surface inpackages/crypto/crypto/src/trust-anchors/:TrustAnchortype —{ bytes: Uint8Array; bundle: PublicBundle<TLayoutFields> }TrustAnchorReaderinterface —read(path: string): Promise<Uint8Array>loadTrustAnchor(reader, path): Promise<TrustAnchor>— reads bytes, parses, self-verifiesloadTrustAnchorFromEnv(reader, getenv?, envVar?): Promise<TrustAnchor>— env-driven path, defaults toSUAVEPLAN_TRUST_ANCHOR_PATH- 3 new error codes:
TRUST_ANCHOR.PATH_MISSING,TRUST_ANCHOR.READ_FAILED,TRUST_ANCHOR.SELF_VERIFY_FAILED - Subpath export
./trust-anchors
- MODIFIED
cryptocapability: extend the existingChain bundle traversalrequirement'sVerified by:list to include the new trust-anchor self-verify scenario (the loader is what produces thetrustAnchorBundleBytesverifyChainconsumes). - This change ships source + tests + docs, not just spec. Every requirement carries a
Verified by:pointer to a real test name, per the convention re-affirmed bycrypto-spec-restore-versioning.
Capabilities
New Capabilities
(none)
Modified Capabilities
crypto: add 1 requirement (Trust-anchor bootstrap); no requirements modified or removed.
Impact
- Affected specs:
openspec/specs/crypto/spec.md(1 requirement added). - Affected code:
packages/crypto/crypto/src/trust-anchors/(new directory — 5 files: types, impl, tests, docs, barrel).packages/crypto/crypto/src/errors/codes/codes.ts(3 codes added).packages/crypto/crypto/src/index.ts(re-export the new surface).packages/crypto/crypto/package.json(subpath export./trust-anchors).packages/crypto/crypto/vite.config.ts(build entry).packages/crypto/crypto/tests/no-duplicates.test.ts(whitelisttrust-anchors/).
- Affected tests: 2 new test files (round-trip, error-code conformance). Coverage gate stays at 100/100/100/100.
- Downstream:
crypto-cliand any daemon that consumes a chain bundle can replace its hand-rolled loader with the shipped one. No breaking change — the existingverifyChainsignature is unchanged; consumers opt in to the loader. - Risk: low. The loader is a thin wrapper over an existing tested verify-path. Filesystem and env access are caller-injected, so the universal package picks up no platform deps.