Files
suaveplan/openspec/archive/2026-05-18-enterprise-server-keys/proposal.md
2026-05-27 23:29:53 +01:00

86 lines
6.7 KiB
Markdown

# Add @suaveplan/server-keys — full-parity port of the 18-bin CLI
**Package**: `@suaveplan/server-keys`
**Status**: PROPOSED
**Type**: migration (full parity with main) + enterprise stack additions
- **Spec delta**: ./specs/server-keys/spec.md
- **Target path**: `packages/security/server-keys/`
## Why
Operator-facing CLI bundle for the Suaveplan server-key lifecycle: root + server keychain generation (with hierarchical `ChainedKeySigner` chains via `@suaveplan/crypto-keys`), encrypted vault persistence, key rotation, vault/key viewing, signed file packet preparation/serving/validation, and a license-key issuance bin (uses the just-shipped `@suaveplan/license`). Upstream at `../suaveplan/packages/server/server-keys/` ships 18 bin entry points + 3 command modules + a shared prompt utility (3,122 LOC total). This migration ports all 18 bins verbatim at the CLI-surface level so operator scripts that invoke `create-root-keys`, `roll-server-keys-chain`, `prepare-file`, etc. continue to work byte-compatibly.
Genesis enterprise stack layered on top: `SuaveplanError`-typed errors with a `SERVER_KEYS.*` code registry, Zod boundary validation for CLI options, OpenTelemetry spans on the long-running operations (file prep/serve/validate, keychain ops), sibling `.md` docs ≥200 words per implementation file, fast-check property tests where pure-data functions exist (packet manifests, key-chain encode/decode), and 100/100/100/100 coverage. Upstream uses `@suaveplan/keys` (the old umbrella package); genesis-side calls are re-pointed to `@suaveplan/crypto` + `@suaveplan/crypto-keys` (the post-split surface) without changing the wire formats.
## What changes
Net-new package at `0.1.0`, ships under `packages/security/server-keys/`. ADDED:
### 18 bin entry points (verbatim CLI surface)
- **Key + chain bins** (`@suaveplan/crypto-keys` chain signer): `create-root-keys`, `create-root-keys-chain`, `create-server-keys`, `create-server-keys-chain`, `roll-server-keys`, `roll-server-keys-chain`, `verify-key-chain`
- **Viewer bins**: `view-keys`, `view-vault`, `view-public-key-file`, `view-chain-public`
- **File transport bins** (`@suaveplan/packer` + `@suaveplan/storage`): `prepare-file`, `serve-file`, `validate-file`, `get-file-stats`, `validate-packet`
- **License bin** (`@suaveplan/license`): `license` — issues / validates / inspects software-license keys
- **Top-level dispatcher**: `suaveplan` (single `commander` entry routing to all 17 sub-commands)
### Internal command modules (the library surface the bins call into)
- `commands/keys/keys.ts` — `createRootKeysCommand`, `createServerKeysCommand`, `rollServerKeysCommand`, `createRootKeysChainCommand`, `createServerKeysChainCommand`, `rollServerKeysChainCommand`, `verifyKeyChainCommand`, viewers
- `commands/files/files.ts` — `prepareFileCommand`, `validateFileCommand`, `getFileStatsCommand`, `validatePacketCommand`
- `commands/files/serve-file.ts` — `serveFileCommand`
- `commands/license/license.ts` — `licenseCommand` (wraps `@suaveplan/license` `LicenseManager`)
- `prompt/prompt.ts` — `promptForPassword`, `promptForNewPassword` (`@inquirer/prompts`)
- `run-function.ts` — top-level error-handling wrapper for `.action(...)` callbacks
### Enterprise stack (additive over upstream)
- `error-codes.ts` — `SERVER_KEYS_*` code registry via `defineCodes("SERVER_KEYS", [...])`, `ServerKeysError extends SuaveplanError<ServerKeysErrorCode>`. Codes cover vault decrypt failures, password mismatch, missing root vault, chain verification failure, packet integrity failure, file IO failures, unknown command, plus license-bin pass-throughs.
- Zod boundary schemas for every CLI option bag.
- OpenTelemetry: counter `server_keys.command_total{command}`, histogram `server_keys.command_duration_ms{command}`, span per command.
- `LoggerLike` from `@suaveplan/types` injectable through every command (defaults to console).
- `AbortSignal` plumbing for long-running file ops.
### Bin registration in `package.json`
The full 18-bin `bin` map matches upstream verbatim so `bun link` / `npm install -g` exposes every operator command. Names:
```text
suaveplan, license, create-root-keys, create-server-keys, roll-server-keys,
create-root-keys-chain, create-server-keys-chain, roll-server-keys-chain,
verify-key-chain, view-keys, view-vault, view-public-key-file,
view-chain-public, prepare-file, serve-file, validate-file, get-file-stats,
validate-packet
```
### Subpath exports
Library subpaths so downstream packages can call the command functions directly (e.g. test harnesses, integration tests): `./commands/keys/keys`, `./commands/files/files`, `./commands/files/serve-file`, `./commands/license/license`, `./prompt/prompt`, `./error-codes`, `./run-function`.
## Out of scope
- **Daemon / always-on serve-file mode.** Upstream's `serve-file` is one-shot reconstruction; genesis preserves that. A long-running HTTP serve mode belongs in a separate `@suaveplan/server-keys-http` follow-up.
- **Re-implementing the upstream wire formats.** The keychain encoding, packet header, and license payload formats all come from their respective packages (`@suaveplan/crypto-keys`, `@suaveplan/packer`, `@suaveplan/license`). server-keys is the CLI wrapper, not the format owner.
- **Cross-platform binary distribution.** Bins run under Bun/Node; we do not ship pkg/nexe-style standalone binaries.
- **Telemetry exporter wiring.** OTel instruments are registered against `@opentelemetry/api`; concrete exporter (OTLP/Prometheus/Jaeger) is the operator's choice.
## Source mapping
| Capability | Upstream source | Notes |
|---|---|---|
| 18 bin entry points + commander routing | `../suaveplan/packages/server/server-keys/src/bin/*.ts` + `bin/index.ts` | verbatim CLI surface |
| Key + chain commands (628 LOC) | `src/commands/keys/keys.ts` | re-pointed from `@suaveplan/keys` → `@suaveplan/crypto-keys` `ChainedKeySigner` |
| File transport commands (311 LOC) | `src/commands/files/files.ts` + `serve-file.ts` | uses `@suaveplan/packer` + `@suaveplan/storage`/`storage-file` |
| Password prompts | `src/prompt/prompt.ts` | `@inquirer/prompts` |
| License bin | `src/bin/license.ts` + `src/commands/license/license.types.ts` | wraps `@suaveplan/license` `LicenseManager` |
| `runFunction` error wrapper | `src/runFunction.ts` | verbatim |
| Enterprise audit (Zod, OTel, structured errors, 100% coverage, sibling .md) | `../suaveplan/enterprise/server-keys.md` | all addressed |
| `ServerKeysError` + `SERVER_KEYS_*` code registry | genesis-original | genesis policy — upstream throws plain `Error` |
## Impact
### Affected dependents
None today — net-new on genesis. First downstream will be operator scripts (out-of-repo) and CI workflows that issue/rotate server keys. The `license` bin transitively pulls in `@suaveplan/license` (shipped today).