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

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 crypto capability: add 1 requirement covering trust-anchor bootstrap. New surface in packages/crypto/crypto/src/trust-anchors/:
    • TrustAnchor type — { bytes: Uint8Array; bundle: PublicBundle<TLayoutFields> }
    • TrustAnchorReader interface — read(path: string): Promise<Uint8Array>
    • loadTrustAnchor(reader, path): Promise<TrustAnchor> — reads bytes, parses, self-verifies
    • loadTrustAnchorFromEnv(reader, getenv?, envVar?): Promise<TrustAnchor> — env-driven path, defaults to SUAVEPLAN_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 crypto capability: extend the existing Chain bundle traversal requirement's Verified by: list to include the new trust-anchor self-verify scenario (the loader is what produces the trustAnchorBundleBytes verifyChain consumes).
  • 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 by crypto-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 (whitelist trust-anchors/).
  • Affected tests: 2 new test files (round-trip, error-code conformance). Coverage gate stays at 100/100/100/100.
  • Downstream: crypto-cli and any daemon that consumes a chain bundle can replace its hand-rolled loader with the shipped one. No breaking change — the existing verifyChain signature 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.