Files
suaveplan/openspec/archive/2026-05-03-restore-crypto-cli-and-allowlist/proposal.md
2026-05-27 23:29:53 +01:00

6.8 KiB

restore-crypto-cli-and-allowlist — keychain CLI, slsa migration, import-allowlist gate

Packages: @suaveplan/crypto-cli (new), @suaveplan/slsa (modified), @suaveplan/crypto-core (allowlist gate) Status: PROPOSED Type: new-package + refactor + conformance gate Carved from: restore-crypto-keychain-packer umbrella (2026-05-03) Depends on: slices 1, 2, and 3 must land first

Why

This terminal slice ships the three pieces that turn the crypto stack from "library that exists" into "library that consumers must use correctly":

  1. @suaveplan/crypto-cli — a keychain binary with eight subcommands. Operators (and CI) drive root + server identity lifecycle through a stable command surface rather than ad-hoc scripts. Distinct exit codes per failure class let operators distinguish "wrong password" from "tampered vault" from "expired key" from "revoked key".
  2. @suaveplan/slsa migration — slsa already calls crypto.ed25519.sign(secretKey, …) directly with raw bytes today. This slice migrates it to Authority.sign(DOMAIN_SLSA_BUILDER_V1, …), with the builder identity issued as a ServerIdentity under a SLSA-builder root. The DSSE envelope wire format is unchanged — the proof is a round-trip equality test against pre-change fixtures.
  3. Import-allowlist conformance gate — the most important piece. scripts/openspec-conformance.ts is extended to fail with error severity when any package outside the allowlist (@suaveplan/crypto, @suaveplan/crypto-cli) imports the cryptographic primitives from crypto-core, crypto-cipher, crypto-curve, or crypto-hash-as-MAC. Encoding helpers from crypto-utils and pure-hash uses from crypto-hash remain open. The allowlist is committed as scripts/conformance/crypto-core-allowlist.yml so additions are reviewable.

Without the gate, future packages will drift back into direct primitive use exactly as slsa did. The gate is the single most important guarantee in the umbrella proposal.

What changes

New: @suaveplan/crypto-cli

Located at packages/crypto/crypto-cli/. Built on @suaveplan/cli + @suaveplan/readline + @suaveplan/crypto. Bin entry: keychain → dist/keychain.js.

Subcommands (eight):

  1. keychain root init — generate root identity, write encrypted vault, emit public bundle
  2. keychain server init — generate server identity under a root, emit signed public bundle, append audit log
  3. keychain server roll — version-bump rotation; --idempotency-key deduped via local file ledger; appends CRL + audit
  4. keychain server revoke — --idempotency-key deduped; appends CRL + audit; no re-issue
  5. keychain inspect — view a packed public keychain (verifies self-signature)
  6. keychain verify — verify any signed envelope against a trust anchor + CRL
  7. keychain export-public — strip secrets, output portable public bundle
  8. keychain restore — re-derive a vault from BIP39 mnemonic + version

Cross-cutting:

  • TTY-only password input via cli's password prompt; --password flag rejected with a security pointer
  • Stdin/stdout streamable; --output json|binary|text
  • 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
  • OTel spans + structured logger output for every command
  • Co-located .md per command file (≥ 200 words, basic + advanced examples)

Modified: @suaveplan/slsa

  • Replace direct crypto.ed25519.sign(secretKey, …) calls with Authority.sign(DOMAIN_SLSA_BUILDER_V1, …)
  • Builder identity constructed from a ServerIdentity issued under a SLSA-builder root
  • Provenance test fixtures regenerated with new construction path; DSSE envelope output unchanged (asserted by round-trip equality test)
  • package.json: add @suaveplan/crypto workspace dep; remove direct @suaveplan/crypto-core signing import (retain @suaveplan/crypto-hash and @suaveplan/crypto-utils for hashing + encoding)
  • New test tests/no-direct-crypto-core.test.ts pins import discipline

New: import-allowlist conformance gate

  • scripts/conformance/crypto-core-allowlist.yml — committed allowlist (@suaveplan/crypto, @suaveplan/crypto-cli)
  • scripts/openspec-conformance.ts — extended to scan every workspace package's src/ for imports of @suaveplan/crypto-core, @suaveplan/crypto-cipher, @suaveplan/crypto-curve, or @suaveplan/crypto-hash MAC subpaths; fails with error severity for any non-allowlisted package
  • scripts/conformance/crypto-core-allowlist.test.ts — exercises positive (allowed) and negative (forbidden) cases against fixture packages

Out of scope

  • HSM / threshold backends for Authority — separate crypto-authority-yubikey, crypto-authority-tpm changes
  • Network-fetched CRL distribution (--fetch-crl over HTTP/OCSP)
  • Trust-anchor distribution mechanism beyond two documented patterns (compiled-in pinned constant; env-var path to a trusted public bundle)
  • SLSA Sigstore Rekor integration migration to share the AuditLog (flagged in umbrella as a follow-up)

Source mapping

Requirement Source Notes
keychain binary surface (8 commands) umbrella ### Requirement: \keychain` binary surface` (crypto-cli/spec.md) Verbatim
TTY-only password input umbrella crypto-cli/spec.md Verbatim
Distinct exit codes per failure class umbrella crypto-cli/spec.md Verbatim
--idempotency-key on state-changing commands umbrella crypto-cli/spec.md Verbatim
Per-command behaviour table umbrella crypto-cli/spec.md Verbatim
slsa builder identity via Authority umbrella slsa/spec.md Verbatim
Import-allowlist gate umbrella crypto-core/spec.md Verbatim

Verification approach

  • 100/100/100/100 coverage on every new command file in crypto-cli
  • Pre-change DSSE envelope fixtures committed; post-migration slsa round-trip equals byte-for-byte
  • Allowlist gate: positive fixtures (crypto, crypto-cli import primitives → green) and negative fixtures (test fixture package importing primitives → red) exercised in tests/
  • TTY mock harness driven via @suaveplan/cli's InputReader interface (no real TTY needed)

Migration impact

  • Every package in genesis whose src/ reaches into crypto-core, crypto-cipher, crypto-curve, or crypto-hash-as-MAC for cryptographic operations MUST migrate to @suaveplan/crypto's Authority/Packer/Vault API or be added to the allowlist (with reviewer approval).
  • After this slice, the gate enforces the discipline mechanically. The 5 packages currently importing @suaveplan/crypto for primitives (crypto-sodium, crypto-browser, slsa, bitpacker-crypto, drive-plugin-chunks, packer) are reviewed in this slice and either migrated or allowlisted with rationale.