# SUPERSEDED — bitpacker-crypto provider abstraction This file records two related decisions: 1. **Original decision (`bitpacker-crypto-port`, 2026-05-02)** — upstream's `packages/universal/bitpacker/src/crypto/` ships ~1,906 lines of provider abstraction (`BaseCryptoProvider`, `CryptoRegistry`, and three adapters: `noop`, `libsodium`, `elliptic`). suaveplan intentionally **does not port that abstraction**. 2. **Replacement (`bitpacker-crypto-rewrite`, 2026-05-02)** — a deeper consumer scan found 8+ call-sites across suaveplan and upstream manually re-implementing the same crypto-with-bitpacker patterns (sign-then-pack, encrypt-pack-frame, versioned envelopes, schema fingerprints, stateless verifiers). Rather than leaving the package as checksums-only, the rewrite **adds five bit-packer-aware primitives** that consolidate those inline duplications. See "What we built instead" at the bottom of this file. The original rejection of upstream's abstraction still stands. The patterns those rejected symbols *attempted* to address are now solved by the new primitives, in shapes that real consumers can actually use. ## Why the abstraction is not ported The upstream provider abstraction is a self-referential layer over `@suaveplan/crypto`: its `LibsodiumCryptoProvider` does `import from "@suaveplan/crypto"`; its `EllipticCryptoProvider` wraps the same primitives via the `elliptic` package; its `NoOpCryptoProvider` is a test harness. A grep across `/mnt/s/web/suaveplan/packages` on 2026-05-02 confirmed **no upstream consumer outside the bitpacker package itself** imports `BaseCryptoProvider`, `CryptoRegistry`, or any adapter — the abstraction was never wired into a production code path. suaveplan 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`. Adding the upstream abstraction would duplicate work, route around the suaveplan structured-error / OTel conventions, and contradict the "no duplicate libraries" directive in CLAUDE.md. ## Rejected upstream surface and its suaveplan replacement | Upstream symbol | Upstream file:lines | suaveplan replacement | |---|---|---| | `BaseCryptoProvider extends EventEmitter` | `crypto/provider/provider.ts:26-232` | None — call `@suaveplan/crypto` directly | | `generateNonce(length)` | `crypto/provider/provider.ts:237-248` | `@suaveplan/crypto-utils::random.bytes(length)` | | `concatBytes(...arrays)` | `crypto/provider/provider.ts:253-262` | `@suaveplan/crypto-utils::bytes.concat(...arrays)` | | `validateKeyLength(key, expected, type)` | `crypto/provider/provider.ts:267-277` | Caller-side guard via `BitpackerError` (no central helper) | | `constantTimeEqual(a, b)` | `crypto/provider/provider.ts:282-289` | `@suaveplan/crypto-utils::bytes.equalsConstantTime(a, b)` | | `secureWipe(buffer)` | `crypto/provider/provider.ts:294-296` | `@suaveplan/crypto-utils::bytes.zeroize(buffer)` | | `LibsodiumCryptoProvider` + `createXChaCha20Provider` + `createAesGcmProvider` | `crypto/adapters/libsodium/libsodium.ts:94-335` | `@suaveplan/crypto-sodium` + `@suaveplan/crypto::aead` | | `EllipticCryptoProvider` + `createSecp256k1Provider` + `createEd25519Provider` + `createP256Provider` | `crypto/adapters/elliptic/elliptic.ts:36-572` | `@suaveplan/crypto-curve` | | `NoOpCryptoProvider` + `createTestProvider` + `createSlowProvider` + `createUnreliableProvider` | `crypto/adapters/noop/noop.ts:35-335` | `@suaveplan/testing` fakes for `@suaveplan/crypto` | | `CryptoRegistry extends EventEmitter` + `createCryptoRegistry` + `getGlobalRegistry` | `crypto/registry/registry.ts:21-370` | None — registry was self-referential | ## What `@suaveplan/bitpacker-crypto` still ships This package is **not deprecated**. It still ships the checksum surface that the upstream `bitpacker/src/core/checksum/` directory provided, all under `@suaveplan/bitpacker-crypto`: - `crc32(data)` and `appendCrc32` / `verifyAndStripCrc32` - `adler32(data)` - `calculateXxhash32`, `packXxhash32`, `unpackXxhash32`, `appendXxhash32`, `verifyXxhash32`, `verifyAndStripXxhash32` Checksums are not part of the rejected provider abstraction; this decision record covers only the `crypto/` directory of the upstream bitpacker source. ## Encrypt-then-pack — recommended replacement pattern If you need to encrypt or sign bytes that go into a bitpacker payload, reach for `@suaveplan/packer` (which ships chunked AEAD already) or call `@suaveplan/crypto` directly. There is no provider/registry indirection . ```typescript import { Packer } from "@suaveplan/packer"; import { aead } from "@suaveplan/crypto"; import { defineBitSchema } from "@suaveplan/bitpacker"; const schema = defineBitSchema({ payload: "~" }); const bytes = schema.pack({ payload: new Uint8Array([1, 2, 3, 4]) }); // Encrypt the packed bytes: const ciphertext = await aead.encrypt({ algorithm: "xchacha20-poly1305", key: myKey, nonce: myNonce, plaintext: bytes, associatedData: new Uint8Array(), }); // Or use @suaveplan/packer for chunked AEAD with a manifest: const packer = new Packer({ schema, key: myKey }); const envelope = await packer.pack({ payload: new Uint8Array([1, 2, 3, 4]) }); ``` ## See also - spec change `bitpacker-crypto-port` — original decision (superseded). - spec change `bitpacker-crypto-rewrite` — replacement decision; **slimmed 2026-05-02** after `restore-crypto-keychain-packer` discovery to two auxiliary primitives only. - spec change `restore-crypto-keychain-packer` — parallel work that owns the new opinionated `@suaveplan/crypto` layer (Packer / Authority / AuditLog / RevocationList). - `bitpacker-modular-restructure` parity table — flips the five corresponding rows from "🚫 not ported" to "⛔ intentionally superseded; see `restore-crypto-keychain-packer` for the consolidation, `bitpacker-crypto-rewrite` for the auxiliary primitives". - `@suaveplan/crypto`, `@suaveplan/crypto-sodium`, `@suaveplan/crypto-browser`, `@suaveplan/crypto-vault`, `@suaveplan/crypto-keys`, `@suaveplan/crypto-utils`, `@suaveplan/packer` — the crypto family. ## What we built instead — bit-packer auxiliary primitives only After the 2026-05-02 audit + the discovery that **`restore-crypto-keychain-packer`** already proposes a new opinionated `@suaveplan/crypto` layer with `Packer` / `Authority` / `AuditLog` / `RevocationList`, this package now ships only the two primitives that don't overlap that work: | Primitive | What it does | Consumer that drove the design | |---|---|---| | `schemaFingerprint(schema, algorithm?, registry?)` | Content-hash a `BitPackerSchema` after canonicalization (sorted keys, no whitespace, deterministic encoding). Algorithm name resolved via `HashAlgorithmRegistry`. Companion `assertSchemaFingerprint` for "do we agree on the schema?" handshakes. | `@suaveplan/network-registry::Xxhash32HashProvider`/`Crc32HashProvider` (upstream port); planned mesh schema-agreement handshake (`docs/network/security-and-tokens.md`, `docs/network/architecture.md`). | | `HashAlgorithmRegistry` | Pluggable hash-algorithm registry. Defaults: `xxhash32`, `crc32`, `sha256`. Consumers register their own (`blake3` for drive whole-file hash, future PQC). User directive on extensibility/plugins/middleware made this a first-class surface. | `docs/drive/encryption.md` L79 (BLAKE3 whole-file hash); user directive (extensibility/plugins). | Plus the existing checksum surface (`crc32`, `adler32`, `xxhash32` and their `appendCrc32`/`verifyAndStripCrc32`/`appendXxhash32`/etc. helpers) is preserved unchanged. ## What lives in `@suaveplan/crypto` instead (via `restore-crypto-keychain-packer`) The patterns my first-draft rewrite proposed are now correctly placed in the new opinionated `@suaveplan/crypto` layer, where they compose with `Authority` + `Keychain` + the closed-list `DomainTag` registry: | First-draft primitive (dropped here) | Owned by `@suaveplan/crypto` (via `restore-crypto-keychain-packer`) | |---|---| | `SignedBitSchema` (Authority + DomainTag) | `Packer` (encrypt-and-sign envelope under DomainTag via Authority) + direct `Authority.sign(domain, slot, payload)` for sign-only | | `EnvelopeSchema(magic, version, body)` | `packPublicBundle` shape + `Packer` envelope | | `HashChainSchema` (Merkle-chained signed log) | `AuditLog` (Merkle log + `proveInclusion` / `verifyInclusion`) | | `MultiSignedBitSchema` (N-of-N co-sign) | If/when `MeshTrustToken` (NET-110) needs it, the spec delta lands in `@suaveplan/crypto` next to `Packer` and `RevocationList` (not here — co-signing belongs in the `Authority`-using crypto layer) | | `EncryptedBitSchema` (niche AEAD) | `Packer` (covers small-payload encrypt-and-sign exactly) | | New domain tags (`DOMAIN_BITPACKER_*_V1`) | `DomainTag` is a closed list owned by `@suaveplan/crypto/domain-tags` — additions are wire-format extensions in that package, not in `bitpacker-crypto` | ### What was dropped (chronological) | Dropped | Why | |---|---| | `ChunkVerifier` (first audit pass) | `@suaveplan/packer::ChunkVerifier` already covers the locked v0x0001 chunk format. | | `SignedBitSchema` taking raw `signKey` (audit-revision) | suaveplan canonical signing surface is `Authority.sign(domain, slot, payload)`. Raw-key API didn't serve real consumers. | | `EnvelopeSchema` with opaque body (audit-revision) | Real consumers (`PublicBundle`, `ChainBundle`) have structured body fields. | | `DomainTagRegistry` open registry (audit-revision) | Conflicts with suaveplan's intentional closed-list `DomainTag` design. | | `SignatureAlgorithmRegistry` (audit-revision) | `Authority` already dispatches on slot algorithm. | | `SignedBitSchema` / `EnvelopeSchema` / `HashChainSchema` / `MultiSignedBitSchema` / `EncryptedBitSchema` (slim revision) | All overlap with `restore-crypto-keychain-packer`'s `Packer` / `AuditLog` / `RevocationList`. suaveplan policy explicitly forbids duplicate libraries. | | New `DOMAIN_BITPACKER_*_V1` constants (slim revision) | Closed-list domain tags belong in `@suaveplan/crypto/domain-tags`, not here. | The two surviving primitives (`SchemaFingerprint`, `HashAlgorithmRegistry`) are pure bit-packer utilities that compose with `restore-crypto-keychain-packer` without conflict.