Files
skills/docs/foundation/bitpacker/bitpacker-crypto/SUPERSEDED.md
2026-05-29 08:20:56 +01:00

10 KiB

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.

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 .

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.