Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 KiB
SUPERSEDED — bitpacker-crypto provider abstraction
This file records two related decisions:
- Original decision (
bitpacker-crypto-port, 2026-05-02) — upstream'spackages/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. - 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)andappendCrc32/verifyAndStripCrc32adler32(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 .
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 afterrestore-crypto-keychain-packerdiscovery to two auxiliary primitives only. - spec change
restore-crypto-keychain-packer— parallel work that owns the new opinionated@suaveplan/cryptolayer (Packer / Authority / AuditLog / RevocationList). bitpacker-modular-restructureparity table — flips the five corresponding rows from "🚫 not ported" to "⛔ intentionally superseded; seerestore-crypto-keychain-packerfor the consolidation,bitpacker-crypto-rewritefor 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.