Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
# Bitpacker crypto port — close the 1,906-line gap
**Package**: `@suaveplan/bitpacker-crypto`
**Status**: SUPERSEDED by `bitpacker-crypto-rewrite` (2026-05-02). Both folders archive together so the decision history reads cleanly.
**Type**: spec-conformance + refactor (deprecate-and-route) OR full port
> **Why this change was superseded** — A deeper consumer scan run after this proposal was authored found 8+ consumers across genesis and upstream manually re-implementing the same crypto-with-bitpacker patterns inline (sign-then-pack, encrypt-pack-frame, schema-versioned envelopes, schema fingerprints, stateless verifiers). The "Option A: deprecate-and-route" framing this change recommended is correct *for the upstream provider abstraction* but does not address the inline duplication. `bitpacker-crypto-rewrite` (2026-05-02) replaces this change with a consumer-driven design that consolidates the patterns into five bit-packer-aware primitives: `SignedBitSchema`, `EncryptedBitSchema`, `EnvelopeSchema`, `SchemaFingerprint`, `ChunkVerifier`. The `SUPERSEDED.md` file and audit test created by this change are PRESERVED under the rewrite (revised framing only); the `bitpacker-modular-restructure` parity-table flips this change introduced are PRESERVED (the upstream provider abstraction is still intentionally not ported).
## Why
The 2026-05-02 audit of `bitpacker-modular-restructure` confirmed five files of upstream's `bitpacker/src/crypto/` are absent from genesis: `crypto/provider/provider.ts` (296 L), `crypto/registry/registry.ts` (370 L), and three adapters under `crypto/adapters/{noop,libsodium,elliptic}/` (337 + 331 + 572 L). Total: ~1,906 L of provider abstraction. The parity table in `bitpacker-modular-restructure/proposal.md` flags every one of them 🚫. That gap has to be closed before this restructure can archive cleanly.
But before authoring a 1,906-line port, this proposal documents three findings that change what "closing the gap" should mean:
1. **No upstream consumer depends on `BaseCryptoProvider` / `CryptoRegistry` outside the bitpacker package itself.** Searched `grep -rlE "BaseCryptoProvider|CryptoRegistry|LibsodiumCryptoProvider|EllipticCryptoProvider" /mnt/s/web/suaveplan/packages | grep -v /bitpacker/` → only one hit, and that hit is a documentation file (`packages/universal/overview/bitpacker.md`), not a code import. Upstream's crypto provider abstraction was speculative API surface; nothing actually called it.
2. **Genesis already ships the underlying primitives in five focused packages**: `@suaveplan/crypto` (multi-algorithm façade), `@suaveplan/crypto-sodium` (libsodium engine), `@suaveplan/crypto-browser` (WebCrypto engine), `@suaveplan/crypto-keys` (keypair generation + Keychain), `@suaveplan/crypto-vault` (BIP39 + Argon2id HD vault + ChainSigner). Upstream's `LibsodiumCryptoProvider` (line 1) literally does `import from "@suaveplan/crypto"` — it is not duplicate primitives, it is a provider-pattern wrapper around them.
3. **Genesis already ships chunked AEAD at a higher level** in `@suaveplan/packer` (chunked `aead/verifier/unpacker/packer/manifest`, 99 tests at 100% coverage). Any caller wanting "encrypt my bitpacked bytes" should use packer, not invent a parallel provider/registry.
A literal port would re-add a provider abstraction that no consumer asked for, while shadowing the crypto family that already exists. That is the opposite of the genesis directive.
## Decision required
Two options. The proposal recommends Option A; Option B is fully scoped here so a reviewer can flip the call at review without restarting the change.
### Option A — Deprecate-and-route (recommended)
Mark the upstream `bitpacker-crypto` provider/registry/adapters as **superseded** and route any future bitpacker-internal crypto need through the existing `@suaveplan/crypto` family. Specifically:
- DO NOT port `BaseCryptoProvider`, `CryptoRegistry`, `LibsodiumCryptoProvider`, `EllipticCryptoProvider`, `NoOpCryptoProvider`, or their type companions.
- DO NOT port `generateNonce` / `concatBytes` / `validateKeyLength` / `constantTimeEqual` / `secureWipe` from upstream `provider.ts` — every one of them already exists in `@suaveplan/crypto-utils` (`bytes.concat`, `bytes.equalsConstantTime`, `random.bytes`, `bytes.zeroize`).
- DO add a `SUPERSEDED.md` file at `packages/bitpacker/bitpacker-crypto/SUPERSEDED.md` recording the decision, the upstream surface that's intentionally not ported, and the genesis replacement for each one.
- DO close the parity-table 🚫 rows in `bitpacker-modular-restructure/proposal.md` by flipping their status to ⛔ "intentionally not ported" with a pointer to this change.
- KEEP the existing `bitpacker-crypto` checksum surface (`crc32`, `adler32`, `xxhash32`, `appendCrc32`, `verifyAndStripCrc32`) — that's not the gap; it's a separate, non-controversial concern.
**Why recommended**: closes the parity gap honestly, avoids 1,906 L of duplicate abstraction, lets future bitpacker callers reach for the crypto family that's already documented in genesis, and matches the genesis directive of "no duplicate libraries."
**Cost**: zero implementation lines. The change is a decision document plus an audit-table flip.
### Option B — Full port
Port the upstream crypto subsystem verbatim into `bitpacker-crypto`, with the genesis-required hardening:
- ADDED: `BaseCryptoProvider extends EventEmitter` abstract class — encrypt / decrypt / sign / verify / deriveKey with event emission and capability advertisement. Replace upstream's plain `Error` throws with `BitpackerError` extending `@suaveplan/error`.
- ADDED: `LibsodiumCryptoProvider` — wraps `@suaveplan/crypto-sodium` (genesis package, not the upstream `@suaveplan/crypto` import); supports XChaCha20-Poly1305, AES-GCM, ed25519 sign / verify, X25519 agree, Argon2id key derivation.
- ADDED: `EllipticCryptoProvider` — wraps `@suaveplan/crypto-curve` (genesis package); supports secp256k1 / ed25519 / p256 sign / verify; key generation; ECDH agree.
- ADDED: `NoOpCryptoProvider` — for tests and "encryption disabled" code paths; passes data through unchanged with provenance markers; `createSlowProvider`, `createUnreliableProvider` factories for fault-injection tests.
- ADDED: `CryptoRegistry extends EventEmitter` — registers and resolves providers by name; emits `provider:registered`, `provider:unregistered`, `provider:replaced` events; supports a global registry singleton.
- ADDED: `CryptoCapabilities` interface and `EncryptedPayload`, `SignatureResult`, `KeyDerivationOptions`, `EncryptConfig`, `DecryptConfig`, `SignConfig`, `VerifyConfig` types.
- ADDED: `BITPACKER.CRYPTO_*` codes (`CRYPTO_KEY_LENGTH_INVALID`, `CRYPTO_PROVIDER_NOT_INITIALIZED`, `CRYPTO_PROVIDER_NOT_REGISTERED`, `CRYPTO_CAPABILITY_UNSUPPORTED`, `CRYPTO_DECRYPT_FAILED`, `CRYPTO_VERIFY_FAILED`).
- REMOVED (vs upstream): the `Math.random()` fallback in `generateNonce` (line 244 upstream) — genesis routes through `@suaveplan/crypto-utils::random.bytes` which uses `crypto.getRandomValues` only and throws on environments without it.
- REMOVED (vs upstream): the duplicate `concatBytes` / `constantTimeEqual` / `secureWipe` — re-export from `@suaveplan/crypto-utils` instead.
**Why this might still be the right call**: if genesis intends to ship a swappable crypto provider abstraction at the bitpacker layer specifically (e.g. so `CompressedBitPacker` can advertise "encryption enabled / disabled" without coupling to `@suaveplan/crypto-vault`), the port encodes that decision durably. It would also un-block any future feature where bitpacker's wire format wants to declare "this payload is signed by provider X with capabilities Y."
**Cost**: roughly 8 implementation files at ~200 L each, ~12 spec-conformance test files, plus the hardening and documentation; estimate ~3,500 L net new (port + hardening + tests + docs). Coverage gate is 100/100/100/100 per the genesis quality bar.
---
The remainder of this proposal is **the spec delta if Option A is chosen**. If Option B is chosen at review, the delta will be re-authored to match the ADDED/MODIFIED/REMOVED lists above and the source-mapping table will cite the upstream files line-by-line.
## What changes (Option A)
- ADDED: `packages/bitpacker/bitpacker-crypto/SUPERSEDED.md` — decision record listing every upstream surface intentionally not ported and naming its genesis replacement.
- MODIFIED: `openspec/changes/bitpacker-modular-restructure/proposal.md` parity table — flip the five 🚫 rows for crypto-provider, crypto-registry, crypto-adapter-noop, crypto-adapter-libsodium, crypto-adapter-elliptic from "🚫 not ported" to "⛔ intentionally superseded by `@suaveplan/crypto*`; see `bitpacker-crypto-port`."
- MODIFIED: `openspec/changes/bitpacker-modular-restructure/tasks.md` — tick §14.6 with a pointer to this change.
- MODIFIED: `packages/bitpacker/bitpacker-crypto/README.md` — add a "Crypto provider needs" section pointing at `@suaveplan/crypto`, `crypto-sodium`, `crypto-browser`, `crypto-vault`, `packer`.
- ADDED: `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts` — pins the decision (`SUPERSEDED.md` exists; the upstream symbol names listed in it do NOT exist as exports of `@suaveplan/bitpacker-crypto`).
## Out of scope
- The `crc32` / `adler32` / `xxhash32` checksum helpers already in `bitpacker-crypto` — those are not part of the upstream provider abstraction and are not affected by this decision.
- `appendAdler32` / `verifyAdler32` helper restoration (tracked in `bitpacker-modular-restructure` §14.7).
- Adding event-emitting wrappers around `@suaveplan/crypto` in any other package — if a downstream consumer genuinely needs event emission on every encrypt / decrypt, that is a separate proposal scoped to that consumer.
- Authoring `openspec/specs/bitpacker/spec.md` (the canonical core spec) — covered by a follow-up change.
## Source mapping
Every requirement in the spec delta below cites upstream-file evidence. Because Option A is a deliberate scope-down, the mapping is "what is intentionally NOT ported" rather than "what we ported".
| Upstream surface (intentionally not ported) | File | Line range | Genesis replacement |
|---|---|---|---|
| `BaseCryptoProvider extends EventEmitter` | `crypto/provider/provider.ts` | L26-L232 | None — direct calls to `@suaveplan/crypto` façade |
| `generateNonce(length)` | `crypto/provider/provider.ts` | L237-L248 | `@suaveplan/crypto-utils::random.bytes(length)` |
| `concatBytes(...arrays)` | `crypto/provider/provider.ts` | L253-L262 | `@suaveplan/crypto-utils::bytes.concat(...arrays)` |
| `validateKeyLength(key, expected, type)` | `crypto/provider/provider.ts` | L267-L277 | Caller-side guard; no central helper needed (genesis throws coded errors at the boundary) |
| `constantTimeEqual(a, b)` | `crypto/provider/provider.ts` | L282-L289 | `@suaveplan/crypto-utils::bytes.equalsConstantTime(a, b)` |
| `secureWipe(buffer)` | `crypto/provider/provider.ts` | L294-L296 | `@suaveplan/crypto-utils::bytes.zeroize(buffer)` |
| `LibsodiumCryptoProvider` + `createXChaCha20Provider` + `createAesGcmProvider` | `crypto/adapters/libsodium/libsodium.ts` | L94-L335 | `@suaveplan/crypto-sodium` (libsodium engine) + `@suaveplan/crypto::aead` |
| `EllipticCryptoProvider` + 3 factory helpers | `crypto/adapters/elliptic/elliptic.ts` | L36-L572 | `@suaveplan/crypto-curve` (secp256k1 / ed25519 / p256) |
| `NoOpCryptoProvider` + `createTestProvider` + `createSlowProvider` + `createUnreliableProvider` | `crypto/adapters/noop/noop.ts` | L35-L335 | Test-only; consumers can stub `@suaveplan/crypto` calls directly with `@suaveplan/testing` fakes |
| `CryptoRegistry extends EventEmitter` + `createCryptoRegistry` + `getGlobalRegistry` | `crypto/registry/registry.ts` | L21-L370 | None — no upstream consumer relied on it; the registry was self-referential |
### Requirements with no source line
| Requirement | Justification |
|---|---|
| `SUPERSEDED.md` audit trail | Genesis policy: every removed surface needs a documented reason. Recording the decision durably in-repo is cheaper than re-litigating it at every future audit. |
| Test that pins the absence of upstream symbols | Defensive: prevents a future contributor from accidentally re-introducing the abstraction without re-running the decision. |
## Verification approach
- `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts` — asserts `BaseCryptoProvider`, `CryptoRegistry`, `LibsodiumCryptoProvider`, `EllipticCryptoProvider`, `NoOpCryptoProvider`, `generateNonce`, `concatBytes`, `validateKeyLength`, `constantTimeEqual`, `secureWipe` are NOT exported from `@suaveplan/bitpacker-crypto`. Each missing symbol is one `it(...)`.
- A grep-based check (run in `tasks.md` §3.3) confirming nothing in `packages/` imports the names above from `@suaveplan/bitpacker-crypto`.
## Migration impact
- **Internal**: zero. The bitpacker-crypto package today only exports checksum helpers; no genesis package depends on the provider abstraction.
- **Future consumers**: anyone wanting to encrypt or sign packed bytes should reach for `@suaveplan/crypto` (façade), `@suaveplan/crypto-sodium` / `crypto-browser` (engines), `@suaveplan/crypto-vault` (HD vault + ChainSigner), or `@suaveplan/packer` (chunked AEAD). The decision record makes that routing visible.
- **Upstream port aspirants** — anyone reading `bitpacker-modular-restructure/proposal.md` and seeing the 🚫 rows will be redirected to this decision instead of attempting a literal port.

View File

@@ -0,0 +1,77 @@
<!-- markdownlint-disable MD024 -->
# Capability Spec Delta: bitpacker-crypto (bitpacker-crypto-port, Option A)
**Package**: `@suaveplan/bitpacker-crypto`
**Status**: PROPOSED — gated on Option A acceptance in `proposal.md`
> Every requirement below carries a `**Verified by:**` line at authoring time. Option A is a deliberate scope-down: the only behavior contract this change adds is "the upstream provider abstraction is intentionally not part of `@suaveplan/bitpacker-crypto`." The tests that pin this contract live next to the package source.
## ADDED Requirements
### Requirement: Decision record exists in-tree
The package SHALL ship a `SUPERSEDED.md` file at the package root documenting (i) every upstream symbol intentionally not ported, (ii) the upstream file and line range it came from, (iii) the genesis replacement (a concrete `@suaveplan/*` package and export), and (iv) the rationale. The file SHALL be at least 200 words and SHALL include one runnable example showing how a caller wanting "encrypt then pack" achieves it via `@suaveplan/packer` + `@suaveplan/crypto`.
#### Scenario: SUPERSEDED.md is present and well-formed
- **WHEN** the file `packages/bitpacker/bitpacker-crypto/SUPERSEDED.md` is read
- **THEN** it exists, is at least 200 words excluding code blocks, contains a section per upstream symbol listed in `proposal.md` source mapping, and contains at least one fenced code block whose first line begins with `import` from `@suaveplan/`
#### Scenario: every parity-table row has a SUPERSEDED.md section
- **WHEN** the parity-table rows in `bitpacker-modular-restructure/proposal.md` flagged "⛔ intentionally superseded; see `bitpacker-crypto-port`" are enumerated (5 rows)
- **THEN** each row has a corresponding section header in `SUPERSEDED.md` and the section names the same upstream file the parity-table row referenced
**Verified by:** `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"SUPERSEDED.md exists and is well-formed"` AND `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"every parity-table row maps to a SUPERSEDED.md section"`
---
### Requirement: Upstream provider symbols are NOT exported
The package SHALL NOT export any of the following symbols by these exact names: `BaseCryptoProvider`, `CryptoRegistry`, `LibsodiumCryptoProvider`, `EllipticCryptoProvider`, `NoOpCryptoProvider`, `generateNonce`, `concatBytes`, `validateKeyLength`, `constantTimeEqual`, `secureWipe`, `createCryptoRegistry`, `getGlobalRegistry`, `createXChaCha20Provider`, `createAesGcmProvider`, `createSecp256k1Provider`, `createEd25519Provider`, `createP256Provider`, `createTestProvider`, `createSlowProvider`, `createUnreliableProvider`. Any future re-introduction of these names SHALL constitute a deliberate spec change reverting this decision.
#### Scenario: rejected symbol cannot be imported
- **WHEN** a TypeScript consumer writes `import { BaseCryptoProvider } from "@suaveplan/bitpacker-crypto";`
- **THEN** the import fails to resolve at typecheck time
#### Scenario: every rejected symbol pinned by an `it`
- **WHEN** `SUPERSEDED.test.ts` runs
- **THEN** there is exactly one `it(...)` per name in the list above and each asserts the runtime export is `undefined`
#### Scenario: parity-table rows show ⛔ status
- **WHEN** the parity table in `bitpacker-modular-restructure/proposal.md` is read after this change archives
- **THEN** the five rows for `Crypto provider`, `Crypto registry`, `Crypto adapter — noop`, `Crypto adapter — libsodium`, `Crypto adapter — elliptic` show "⛔ intentionally superseded; see `bitpacker-crypto-port`" with no remaining 🚫 marker
**Verified by:** `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"BaseCryptoProvider is not a runtime export"` AND `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"CryptoRegistry is not a runtime export"` AND `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"LibsodiumCryptoProvider is not a runtime export"` AND `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"EllipticCryptoProvider is not a runtime export"` AND `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"NoOpCryptoProvider is not a runtime export"` AND `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"generateNonce is not a runtime export"` AND `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"concatBytes is not a runtime export"` AND `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"validateKeyLength is not a runtime export"` AND `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"constantTimeEqual is not a runtime export"` AND `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"secureWipe is not a runtime export"`
---
### Requirement: README routes future callers to the genesis crypto family
The package's `README.md` SHALL contain a "Crypto provider needs?" section that names the genesis replacement packages (`@suaveplan/crypto`, `@suaveplan/crypto-sodium`, `@suaveplan/crypto-browser`, `@suaveplan/crypto-vault`, `@suaveplan/packer`) and links to each. The section SHALL be ≤120 words and SHALL NOT document the rejected upstream API.
#### Scenario: README routing section is present
- **WHEN** `packages/bitpacker/bitpacker-crypto/README.md` is read
- **THEN** it contains a level-2 heading `## Crypto provider needs?` and the body names every package listed above with a markdown link
#### Scenario: README does not document the rejected API
- **WHEN** the README is grepped for `BaseCryptoProvider`, `CryptoRegistry`, or `LibsodiumCryptoProvider`
- **THEN** there are zero matches outside of the link to `SUPERSEDED.md`
**Verified by:** `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"README has Crypto provider needs section linking the replacement packages"` AND `packages/bitpacker/bitpacker-crypto/SUPERSEDED.test.ts::"README does not document the rejected upstream API"`
## REMOVED Requirements
### Requirement: Provider abstraction port from upstream `bitpacker/src/crypto/`
**Reason:** Upstream's `BaseCryptoProvider` / `CryptoRegistry` / three adapters (`libsodium`, `elliptic`, `noop`) are a self-referential abstraction layer over `@suaveplan/crypto`. No upstream consumer outside the bitpacker package itself imports any of them (verified 2026-05-02 by grep across `/mnt/s/web/suaveplan/packages`). Genesis already ships every underlying primitive in five focused packages (`@suaveplan/crypto`, `crypto-sodium`, `crypto-browser`, `crypto-vault`, `crypto-keys`) plus a higher-level chunked AEAD in `@suaveplan/packer`. Re-adding the abstraction would duplicate that work, conflict with the genesis structured-error / OTel conventions, and contradict the "no duplicate libraries" directive in CLAUDE.md.
**Migration:** Future bitpacker callers should reach for the genesis crypto family directly. See `SUPERSEDED.md` for the per-symbol replacement table and an "encrypt-then-pack" example using `@suaveplan/packer` + `@suaveplan/crypto`.
<!-- markdownlint-enable MD024 -->

View File

@@ -0,0 +1,11 @@
# Tasks — Bitpacker crypto port (Option A: deprecate-and-route)
> **STATUS — 2026-05-02:** This change is **SUPERSEDED** by `bitpacker-crypto-rewrite` (slim) and `restore-crypto-keychain-packer`. The decision documented in `proposal.md` is preserved as decision history. All checkboxes are marked complete because the work was reframed and routed through the supersedor changes; no Option A or Option B path is being executed under this change-id.
- [x] 1.1 SUPERSEDED — Decision moved to `restore-crypto-keychain-packer` + slimmed `bitpacker-crypto-rewrite`.
- [x] 1.2 N/A — Neither Option A nor Option B is being executed under this change-id.
- [x] 1.3 N/A — Routed via the supersedor changes.
## Closeout
- [x] All remaining tasks deferred to supersedor changes; this folder archives with a status note for decision-history continuity.