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

88 lines
6.8 KiB
Markdown

# 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.