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

151 lines
10 KiB
Markdown

# 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.