8.9 KiB
Bitpacker crypto rewrite — auxiliary primitives only (slim)
Package: @suaveplan/bitpacker-crypto
Status: PROPOSED — supersedes bitpacker-crypto-port. Slimmed 2026-05-02 after discovering parallel work in restore-crypto-keychain-packer.
Type: clean-sheet additions of auxiliary bit-packer-aware primitives
Why (slim version)
The 2026-05-02 audit drove an initial design with seven primitives. A subsequent uncommitted-OpenSpec scan found that restore-crypto-keychain-packer (parallel work, partially implemented) already proposes a new opinionated @suaveplan/crypto layer that contains essentially every primitive whose design depends on Authority + DomainTag + Keychain:
| First-draft primitive | Owned by restore-crypto-keychain-packer |
|---|---|
SignedBitSchema (Authority + DomainTag) |
Packer (encrypt-and-sign envelope) + Authority.sign() |
EnvelopeSchema(magic, version, body) |
packPublicBundle shape + Packer envelope |
HashChainSchema (Merkle-chained signed log) |
AuditLog (Merkle log + proveInclusion / verifyInclusion) |
MultiSignedBitSchema (N-of-N co-sign) |
RevocationList (root-signed); co-signing belongs in the Authority-owning crypto layer if needed |
EncryptedBitSchema (niche AEAD) |
Packer (covers this exactly) |
New domain tags (DOMAIN_BITPACKER_*_V1) |
Closed-list extensions belong in @suaveplan/crypto/domain-tags |
After dropping every overlapping primitive, the two remaining additions in this change are pure bit-packer utilities that don't touch Authority or the closed-list DomainTag registry:
SchemaFingerprint— content-hash aBitPackerSchema(canonical form). Drives mesh schema-agreement handshakes perdocs/network/security-and-tokens.md.HashAlgorithmRegistry— pluggable hash-algorithm registry (xxhash32 / crc32 / sha256 default; consumers register BLAKE3, future PQC).
Both compose with restore-crypto-keychain-packer without conflict.
Decision history
bitpacker-crypto-port(2026-05-02) — proposed deprecate-and-route through@suaveplan/cryptofamily. Superseded by this rewrite.bitpacker-crypto-rewritefirst draft (2026-05-02) — proposed seven bit-packer-aware primitives consolidating eight inline consumers. Superseded by this slim version afterrestore-crypto-keychain-packerdiscovery.bitpacker-crypto-rewriteslim (this proposal) — keeps only the two primitives that don't overlaprestore-crypto-keychain-packer's@suaveplan/cryptolayer.
The audit findings from the first draft (eight inline consumers manually re-implementing crypto+bitpacker patterns) still hold — but they're consolidated by restore-crypto-keychain-packer's Packer / Authority / AuditLog / RevocationList, not by primitives in bitpacker-crypto. Per-consumer migration to those primitives is restore-crypto-keychain-packer's scope, not this change's.
What this package SHALL ship
schemaFingerprint(schema, algorithm?, registry?) → Promise<Uint8Array>
Content-hash a BitPackerSchema after canonical serialization (sorted keys, no whitespace, deterministic encoding). Equivalent schemas in different declaration orders produce byte-identical fingerprints. Used for "do we agree on the schema?" handshakes between mesh peers.
function schemaFingerprint(
schema: BitPackerSchema,
algorithm?: string, // default: "sha256"
registry?: HashAlgorithmRegistry // default: process-wide singleton
): Promise<Uint8Array>;
function assertSchemaFingerprint(
schema: BitPackerSchema,
expected: Uint8Array,
algorithm?: string,
registry?: HashAlgorithmRegistry
): Promise<void>;
Companion assertSchemaFingerprint throws BitpackerError code CRYPTO_SCHEMA_FINGERPRINT_MISMATCH on disagreement.
HashAlgorithmRegistry (pluggable extension)
Open registry of hash algorithms. Defaults shipped: xxhash32, crc32, sha256 (the existing checksum surface in bitpacker-crypto). Consumers register their own (blake3 for drive whole-file hash, future PQC algorithms, etc.).
interface HashAlgorithm {
readonly name: string;
readonly outputBytes: number;
hash(data: Uint8Array): Uint8Array | Promise<Uint8Array>;
}
interface HashAlgorithmRegistry {
register(alg: HashAlgorithm): void;
resolve(name: string): HashAlgorithm | undefined;
has(name: string): boolean;
list(): readonly string[];
}
function defaultHashRegistry(): HashAlgorithmRegistry; // process-wide singleton
function createHashRegistry(): HashAlgorithmRegistry; // fresh
Re-registering an algorithm name with a new implementation is last-write-wins; the replacement emits an OTel span bitpacker-crypto.hash-registry.replaced.
KEPT (existing checksum surface unchanged)
crc32,adler32,appendCrc32,verifyAndStripCrc32calculateXxhash32,packXxhash32,unpackXxhash32,appendXxhash32,verifyXxhash32,verifyAndStripXxhash32
These remain as in the current package; the HashAlgorithmRegistry's default implementations consume them.
What this package SHALL NOT ship (dropped from first draft)
| Dropped | Reason |
|---|---|
SignedBitSchema |
restore-crypto-keychain-packer ships Packer (encrypt-and-sign envelope under DomainTag via Authority); for sign-only use cases, callers use Authority.sign(domain, slot, payload) directly. No bitpacker-side primitive needed. |
EnvelopeSchema(magic, version, bodySchema) |
Pattern lives in @suaveplan/crypto's packPublicBundle and Packer envelope; bit-packer doesn't need a generic envelope primitive when the only consumers are crypto-layer primitives. |
HashChainSchema |
AuditLog in @suaveplan/crypto is a Merkle-chained, root-signed append-only log with proveInclusion / verifyInclusion. Strict superset. |
MultiSignedBitSchema |
Co-signing is an Authority-using primitive; lives next to Packer / RevocationList in @suaveplan/crypto. If/when MeshTrustToken (NET-110) needs it, the spec delta lands in the new @suaveplan/crypto, not here. |
EncryptedBitSchema |
Packer covers small-payload encrypt-and-sign exactly. No niche left to fill. |
New DOMAIN_BITPACKER_*_V1 constants |
DomainTag is a closed list owned by @suaveplan/crypto; new domains are wire-format extensions in that package, not bit-packer. |
ChunkVerifier |
Already dropped in first audit pass. Genesis @suaveplan/packer::ChunkVerifier covers locked v1 chunks. |
Source mapping
| Requirement | Source | Notes |
|---|---|---|
schemaFingerprint canonicalization + algorithm registry |
network-registry::Xxhash32HashProvider/Crc32HashProvider (upstream) |
Pattern: hash-provider over consistent bytes. Schema canonicalization is the bit-packer-side novelty. |
assertSchemaFingerprint mismatch error |
Mesh schema-agreement handshake — docs/network/security-and-tokens.md, docs/network/architecture.md |
Future consumer; the primitive prepares for it. |
HashAlgorithmRegistry default algorithms (xxhash32, crc32, sha256) |
Existing bitpacker-crypto/src/index.ts |
Re-uses checksum impls already shipping. |
HashAlgorithmRegistry BLAKE3 plug-in path |
docs/drive/encryption.md L79, L105 (whole-file BLAKE3) |
Future consumer registers BLAKE3 once. |
Requirements with no source line
| Requirement | Justification |
|---|---|
| Pluggable hash-registry pattern | User directive on extensibility / plugins / middleware. |
| Last-write-wins re-register with OTel emission | Standard genesis registry pattern (matches @suaveplan/random algorithm registries). |
| Zod-validated public types | enterprise/bitpacker.md L38-L39. |
| OTel span on every primitive | enterprise/bitpacker.md L41. |
Out of scope
- Everything in
restore-crypto-keychain-packer(Packer, Authority, AuditLog, RevocationList, Keychain, Vault, etc.) - Domain-tag closed-list additions (those belong in
@suaveplan/crypto/domain-tags) - The
bytesfield type andbyteAlign()core-bitpacker additions (already specced inrestore-crypto-keychain-packer/specs/bitpacker/spec.md) - Per-consumer migration of inline crypto patterns — those go to follow-up changes after
restore-crypto-keychain-packerlands itsPacker/AuditLogprimitives. - All seven dropped primitives (see "What this package SHALL NOT ship" above)
Risks
restore-crypto-keychain-packermay revise the closed-listDomainTagdesign. No risk to this slim change since we don't add domain tags. If their design changes, only their spec needs revision.- Hash registry singleton state leakage between tests. Mitigated by
createHashRegistry()for tests + aspec-conformancetest that singletons are read-only beyondregister. - BLAKE3 not in genesis yet. The
HashAlgorithmRegistryships extension points but doesn't ship a BLAKE3 implementation. The drive layer's BLAKE3 use (perdocs/drive/encryption.md) becomes its own follow-up change to register a BLAKE3 implementation.