# 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 a `BitPackerSchema` (canonical form). Drives mesh schema-agreement handshakes per `docs/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 1. **`bitpacker-crypto-port`** (2026-05-02) — proposed deprecate-and-route through `@suaveplan/crypto` family. Superseded by this rewrite. 2. **`bitpacker-crypto-rewrite` first draft** (2026-05-02) — proposed seven bit-packer-aware primitives consolidating eight inline consumers. Superseded by this slim version after `restore-crypto-keychain-packer` discovery. 3. **`bitpacker-crypto-rewrite` slim** (this proposal) — keeps only the two primitives that don't overlap `restore-crypto-keychain-packer`'s `@suaveplan/crypto` layer. 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` 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. ```ts function schemaFingerprint( schema: BitPackerSchema, algorithm?: string, // default: "sha256" registry?: HashAlgorithmRegistry // default: process-wide singleton ): Promise; function assertSchemaFingerprint( schema: BitPackerSchema, expected: Uint8Array, algorithm?: string, registry?: HashAlgorithmRegistry ): Promise; ``` 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.). ```ts interface HashAlgorithm { readonly name: string; readonly outputBytes: number; hash(data: Uint8Array): Uint8Array | Promise; } 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`, `verifyAndStripCrc32` - `calculateXxhash32`, `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 `bytes` field type and `byteAlign()` core-bitpacker additions (already specced in `restore-crypto-keychain-packer/specs/bitpacker/spec.md`) - Per-consumer migration of inline crypto patterns — those go to follow-up changes after `restore-crypto-keychain-packer` lands its `Packer` / `AuditLog` primitives. - All seven dropped primitives (see "What this package SHALL NOT ship" above) ## Risks 1. **`restore-crypto-keychain-packer` may revise the closed-list `DomainTag` design.** No risk to this slim change since we don't add domain tags. If their design changes, only their spec needs revision. 2. **Hash registry singleton state leakage between tests.** Mitigated by `createHashRegistry()` for tests + a `spec-conformance` test that singletons are read-only beyond `register`. 3. **BLAKE3 not in genesis yet.** The `HashAlgorithmRegistry` ships extension points but doesn't ship a BLAKE3 implementation. The drive layer's BLAKE3 use (per `docs/drive/encryption.md`) becomes its own follow-up change to register a BLAKE3 implementation.