Initial Commit
This commit is contained in:
131
openspec/archive/2026-05-02-bitpacker-crypto-rewrite/proposal.md
Normal file
131
openspec/archive/2026-05-02-bitpacker-crypto-rewrite/proposal.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# 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<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.
|
||||
|
||||
```ts
|
||||
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.).
|
||||
|
||||
```ts
|
||||
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`, `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.
|
||||
@@ -0,0 +1,101 @@
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
# Capability Spec Delta: bitpacker-crypto (bitpacker-crypto-rewrite, slim)
|
||||
|
||||
**Package**: `@suaveplan/bitpacker-crypto`
|
||||
**Status**: PROPOSED — slimmed 2026-05-02 after `restore-crypto-keychain-packer` discovery
|
||||
|
||||
> Every requirement carries a `**Verified by:**` line at authoring time.
|
||||
> Slim version: only the two primitives that don't overlap `restore-crypto-keychain-packer`'s new `@suaveplan/crypto` layer.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: SchemaFingerprint canonical hash of a BitPackerSchema
|
||||
|
||||
The package SHALL expose `schemaFingerprint(schema, algorithm?, registry?) → Promise<Uint8Array>` that returns the chosen hash of a canonical serialization of the schema. Canonicalization SHALL sort object keys, omit whitespace, and produce identical bytes for any two schemas that describe the same wire layout regardless of property declaration order. Default `algorithm` SHALL be `"sha256"`. Default `registry` SHALL be the process-wide `defaultHashRegistry()` singleton. Unknown algorithm names SHALL throw `BitpackerError` with code `CRYPTO_HASH_ALGORITHM_UNKNOWN` and `context.algorithm` naming the missing algorithm.
|
||||
|
||||
The package SHALL also expose `assertSchemaFingerprint(schema, expected, algorithm?, registry?) → Promise<void>` that throws `BitpackerError` with code `CRYPTO_SCHEMA_FINGERPRINT_MISMATCH` on disagreement; the error context SHALL include both fingerprints (hex-encoded).
|
||||
|
||||
#### Scenario: equal schemas in different declaration orders fingerprint identically
|
||||
|
||||
- **WHEN** schema A is `{ a: "u8", b: "u16" }` and schema B is `{ b: "u16", a: "u8" }`
|
||||
- **THEN** `schemaFingerprint(A)` deep-equals `schemaFingerprint(B)` for every default algorithm
|
||||
|
||||
#### Scenario: different schemas fingerprint differently
|
||||
|
||||
- **WHEN** schema A is `{ a: "u8", b: "u16" }` and schema C is `{ a: "u8", b: "u32" }`
|
||||
- **THEN** `schemaFingerprint(A)` is NOT byte-identical to `schemaFingerprint(C)`
|
||||
|
||||
#### Scenario: assertSchemaFingerprint rejects on mismatch
|
||||
|
||||
- **WHEN** `assertSchemaFingerprint(localSchema, remoteFingerprint)` is called and the schemas disagree
|
||||
- **THEN** the call throws `BitpackerError` with code `CRYPTO_SCHEMA_FINGERPRINT_MISMATCH`
|
||||
|
||||
#### Scenario: unknown algorithm rejected
|
||||
|
||||
- **WHEN** `schemaFingerprint(schema, "magic-hash")` is called and `magic-hash` is not registered
|
||||
- **THEN** the call throws `BitpackerError` with code `CRYPTO_HASH_ALGORITHM_UNKNOWN` and `context.algorithm === "magic-hash"`
|
||||
|
||||
#### Scenario: BLAKE3 plugin works after registration
|
||||
|
||||
- **WHEN** a custom `HashAlgorithm` named `"blake3"` is registered into a fresh registry and `schemaFingerprint(schema, "blake3", registry)` is called
|
||||
- **THEN** the result is the BLAKE3 hash of the canonical schema bytes
|
||||
|
||||
**Verified by:** `packages/bitpacker/bitpacker-crypto/src/fingerprint/schema-fingerprint.test.ts::"equal schemas in different orders fingerprint identically"` AND `packages/bitpacker/bitpacker-crypto/src/fingerprint/schema-fingerprint.test.ts::"different schemas fingerprint differently"` AND `packages/bitpacker/bitpacker-crypto/src/fingerprint/schema-fingerprint.test.ts::"assertSchemaFingerprint rejects on mismatch with CRYPTO_SCHEMA_FINGERPRINT_MISMATCH"` AND `packages/bitpacker/bitpacker-crypto/src/fingerprint/schema-fingerprint.spec-conformance.test.ts::"unknown algorithm throws CRYPTO_HASH_ALGORITHM_UNKNOWN"` AND `packages/bitpacker/bitpacker-crypto/src/fingerprint/schema-fingerprint.spec-conformance.test.ts::"plugged-in BLAKE3 algorithm produces BLAKE3 fingerprint"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: HashAlgorithmRegistry as pluggable extension point
|
||||
|
||||
The package SHALL expose a `HashAlgorithmRegistry` interface with `register(alg)`, `resolve(name)`, `has(name)`, and `list()` methods, a `HashAlgorithm` interface with `{ name, outputBytes, hash(data) }`, a `defaultHashRegistry()` returning a process-wide singleton, and a `createHashRegistry()` returning a fresh registry. The default registry SHALL ship `xxhash32`, `crc32`, and `sha256` implementations sourced from the existing checksum surface in this package. Registering an algorithm with a name already present SHALL replace the existing entry (last-write-wins) AND emit an OTel span `bitpacker-crypto.hash-registry.replaced` with the replaced algorithm's `outputBytes` attribute.
|
||||
|
||||
#### Scenario: defaults are present out of the box
|
||||
|
||||
- **WHEN** `defaultHashRegistry()` is called and `has("sha256")`, `has("xxhash32")`, `has("crc32")` are checked
|
||||
- **THEN** all three return `true`
|
||||
|
||||
#### Scenario: register replaces existing algorithm
|
||||
|
||||
- **WHEN** a new `HashAlgorithm` named `"sha256"` (e.g. a faster impl) is registered into a fresh registry already containing the default `sha256`
|
||||
- **THEN** subsequent `resolve("sha256")` returns the new impl AND an OTel span `bitpacker-crypto.hash-registry.replaced` is emitted
|
||||
|
||||
#### Scenario: createHashRegistry isolates from singleton
|
||||
|
||||
- **WHEN** `createHashRegistry()` returns a fresh registry and a custom algorithm is registered into it
|
||||
- **THEN** the singleton's `list()` does NOT include the custom algorithm
|
||||
|
||||
#### Scenario: list returns registered algorithm names
|
||||
|
||||
- **WHEN** `defaultHashRegistry().list()` is called on a fresh process
|
||||
- **THEN** the result contains `"sha256"`, `"xxhash32"`, `"crc32"` (order not guaranteed)
|
||||
|
||||
**Verified by:** `packages/bitpacker/bitpacker-crypto/src/hash-registry/hash-registry.test.ts::"default registry ships sha256, xxhash32, crc32"` AND `packages/bitpacker/bitpacker-crypto/src/hash-registry/hash-registry.test.ts::"register replaces existing algorithm with last-write-wins"` AND `packages/bitpacker/bitpacker-crypto/src/hash-registry/hash-registry.test.ts::"list returns registered algorithm names"` AND `packages/bitpacker/bitpacker-crypto/src/hash-registry/hash-registry.spec-conformance.test.ts::"createHashRegistry isolates from singleton"` AND `packages/bitpacker/bitpacker-crypto/src/hash-registry/hash-registry.spec-conformance.test.ts::"replacement emits OTel hash-registry.replaced span"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Determinism — no module-scope Math.random / Date.now
|
||||
|
||||
The package SHALL contain zero `Math.random()` and zero `Date.now()` calls in implementation files. Time-dependent code (e.g. OTel histogram timestamps) SHALL read from an injected `Clock` from `@suaveplan/types` (default `defaultClock` from `@suaveplan/bitpacker-network`'s convention). Random-dependent code (none currently) SHALL accept an injectable `Rng`.
|
||||
|
||||
#### Scenario: no Math.random / Date.now in implementation files
|
||||
|
||||
- **WHEN** `grep -rE "Math\\.random\\(\\)|Date\\.now\\(\\)" packages/bitpacker/bitpacker-crypto/src/` excluding `.test.ts` and `.spec-conformance.test.ts` is run
|
||||
- **THEN** there are zero matches
|
||||
|
||||
**Verified by:** `packages/bitpacker/bitpacker-crypto/src/fingerprint/schema-fingerprint.spec-conformance.test.ts::"no Math.random or Date.now in implementation files"`
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: Upstream provider/registry abstraction (BaseCryptoProvider, CryptoRegistry, three adapters)
|
||||
|
||||
**Reason:** Upstream's 1,906-line `bitpacker/src/crypto/` provider abstraction has zero non-self consumers. Real consumers re-implement crypto-with-bitpacker patterns inline. Those patterns are now consolidated by `restore-crypto-keychain-packer`'s new `@suaveplan/crypto` layer (`Packer` / `Authority` / `AuditLog` / `RevocationList`), not by primitives in `bitpacker-crypto`.
|
||||
|
||||
**Migration:** The auxiliary primitives in this slim rewrite (`SchemaFingerprint`, `HashAlgorithmRegistry`) provide bit-packer-side utilities that compose with `restore-crypto-keychain-packer` without overlap. `SUPERSEDED.md` records the per-symbol replacement table for the rejected upstream provider abstraction.
|
||||
|
||||
### Requirement: First-draft primitives now owned by restore-crypto-keychain-packer
|
||||
|
||||
**Reason:** The first draft of this rewrite proposed seven primitives. Five of them (`SignedBitSchema`, `EnvelopeSchema`, `HashChainSchema`, `MultiSignedBitSchema`, `EncryptedBitSchema`) overlap with primitives proposed by `restore-crypto-keychain-packer` (`Packer`, `packPublicBundle`, `AuditLog`, `RevocationList`). Genesis policy explicitly forbids duplicate libraries; the slim rewrite drops the overlapping primitives.
|
||||
|
||||
**Migration:** Future consumers needing those patterns reach for the `@suaveplan/crypto` primitives once `restore-crypto-keychain-packer` lands. The audit findings (eight inline consumers manually re-implementing the patterns) carry over as the consumer list for that change.
|
||||
|
||||
<!-- markdownlint-enable MD024 -->
|
||||
@@ -0,0 +1,92 @@
|
||||
# Tasks — Bitpacker crypto rewrite (slim)
|
||||
|
||||
> Slimmed 2026-05-02 after `restore-crypto-keychain-packer` discovery.
|
||||
> Two primitives only: `schemaFingerprint` + `HashAlgorithmRegistry`.
|
||||
> Every checkbox MUST reflect work actually done in this change.
|
||||
|
||||
## 1. Spec sign-off & validation
|
||||
|
||||
- [x] 1.1 Slim proposal authored (this version)
|
||||
- [x] 1.2 Slim spec delta authored (`specs/bitpacker-crypto/spec.md`)
|
||||
- [x] 1.3 Slim tasks authored (this file)
|
||||
- [x] 1.4 `openspec change validate bitpacker-crypto-rewrite --strict` exits clean
|
||||
- [x] 1.5 User sign-off on the slim API shape
|
||||
|
||||
## 2. Decision-history maintenance
|
||||
|
||||
- [x] 2.1 Update `packages/bitpacker/bitpacker-crypto/SUPERSEDED.md` "What we built instead" table to show only `SchemaFingerprint` + `HashAlgorithmRegistry`. Drop the five primitives that moved to `restore-crypto-keychain-packer`. Add a "What lives in `@suaveplan/crypto` instead" cross-reference.
|
||||
- [x] 2.2 Update `bitpacker-modular-restructure/proposal.md` parity-table notes (5 crypto rows) — point at `restore-crypto-keychain-packer` for `Packer` / `Authority` / `AuditLog` / `RevocationList`; mention the slim auxiliary primitives in `bitpacker-crypto-rewrite`.
|
||||
- [x] 2.3 Update `bitpacker-modular-restructure/tasks.md` §14.6 — point at both changes (`restore-crypto-keychain-packer` for the main consolidation, `bitpacker-crypto-rewrite` for the auxiliary primitives).
|
||||
|
||||
## 3. Package metadata
|
||||
|
||||
- [x] 3.1 Update `packages/bitpacker/bitpacker-crypto/package.json` `dependencies`: add `@suaveplan/types`, `@suaveplan/error`, `@opentelemetry/api`, `zod`. (No `@suaveplan/crypto` dep — slim rewrite is independent of crypto layer.)
|
||||
- [x] 3.2 Run `bun install`; verify lockfile updates clean.
|
||||
- [x] 3.3 Run `bun scripts/folder-reorg/generate-manifest.ts`; verify manifest is acyclic.
|
||||
|
||||
## 4. Error codes
|
||||
|
||||
- [x] 4.1 Add to `packages/bitpacker/bitpacker/src/error-codes.ts`: `CRYPTO_HASH_ALGORITHM_UNKNOWN`, `CRYPTO_SCHEMA_FINGERPRINT_MISMATCH`.
|
||||
- [x] 4.2 Each new code must be thrown by ≥ 1 production code path in this change.
|
||||
|
||||
## 5. `HashAlgorithmRegistry` — pluggable extension point
|
||||
|
||||
- [x] 5.1 Author `packages/bitpacker/bitpacker-crypto/src/hash-registry/hash-registry.ts`.
|
||||
- [x] 5.2 Public types: `HashAlgorithm` (`name`, `outputBytes`, `hash(data)`), `HashAlgorithmRegistry` (`register`, `resolve`, `has`, `list`).
|
||||
- [x] 5.3 Default impls for `xxhash32`, `crc32`, `sha256` (re-using existing `bitpacker-crypto` checksums; `sha256` via the existing `@noble/hashes` impl OR via Web Crypto API — pick the env-portable path).
|
||||
- [x] 5.4 `defaultHashRegistry()` returns a process-wide singleton; `createHashRegistry()` returns a fresh registry seeded with the same defaults.
|
||||
- [x] 5.5 Re-register replaces with last-write-wins; emit OTel span `bitpacker-crypto.hash-registry.replaced` with the prior algorithm's `outputBytes` as an attribute.
|
||||
- [x] 5.6 Sibling `hash-registry.md` ≥ 200 words.
|
||||
|
||||
## 6. `schemaFingerprint` + `assertSchemaFingerprint`
|
||||
|
||||
- [x] 6.1 Author `packages/bitpacker/bitpacker-crypto/src/fingerprint/schema-fingerprint.ts`.
|
||||
- [x] 6.2 Canonicalize: sort object keys, no whitespace, deterministic encoding for `BitPackerSchema` shapes (`object`, `array`, primitives, `_optional`, `_oneOf`, `_conditional`, `_version`, `_ref`, etc.).
|
||||
- [x] 6.3 Default algorithm `"sha256"`; resolve via `HashAlgorithmRegistry`.
|
||||
- [x] 6.4 `assertSchemaFingerprint` throws `BitpackerError` code `CRYPTO_SCHEMA_FINGERPRINT_MISMATCH` on disagreement; unknown algorithm throws `CRYPTO_HASH_ALGORITHM_UNKNOWN`.
|
||||
- [x] 6.5 OTel span `bitpacker-crypto.fingerprint`.
|
||||
- [x] 6.6 Sibling `schema-fingerprint.md` ≥ 200 words.
|
||||
|
||||
## 7. Tests
|
||||
|
||||
- [x] 7.1 `hash-registry/hash-registry.test.ts` — defaults present, register replaces with last-write-wins, list returns registered names.
|
||||
- [x] 7.2 `hash-registry/hash-registry.spec-conformance.test.ts` — `createHashRegistry` isolates from singleton, replacement emits OTel `bitpacker-crypto.hash-registry.replaced` span (asserted via real in-memory `MeterProvider`).
|
||||
- [x] 7.3 `fingerprint/schema-fingerprint.test.ts` — equal schemas in different declaration orders fingerprint identically; different schemas fingerprint differently; `assertSchemaFingerprint` throws on mismatch.
|
||||
- [x] 7.4 `fingerprint/schema-fingerprint.spec-conformance.test.ts` — unknown algorithm throws coded error; BLAKE3 plug-in works after registration; no `Math.random`/`Date.now` in implementation files.
|
||||
- [x] 7.5 Existing `superseded.test.ts` re-runs green (the upstream provider symbols are still absent under the rewrite).
|
||||
- [x] 7.6 Every `it(...)` carries `{ timeout: 5000 }`.
|
||||
|
||||
## 8. Pre-archive verification (5-gate)
|
||||
|
||||
- [x] 8.1 `bun run check` — Biome 0/0/0 across the monorepo.
|
||||
- [x] 8.2 `bunx turbo lint typecheck test --filter=@suaveplan/bitpacker-crypto` — green.
|
||||
- [x] 8.3 `bunx turbo test:coverage --filter=@suaveplan/bitpacker-crypto` — 100/100/100/100.
|
||||
- [x] 8.4 `bun scripts/openspec-conformance.ts @suaveplan/bitpacker-crypto` — exit 0.
|
||||
- [x] 8.5 `bunx turbo build --filter=...@suaveplan/bitpacker-crypto` — every dependent compiles green (no consumer breakage).
|
||||
- [x] 8.6 Grep guard: `! grep -rE "Math\\.random\\(\\)|Date\\.now\\(\\)" packages/bitpacker/bitpacker-crypto/src/ --include="*.ts" | grep -v "\\.test\\.ts"` — zero matches.
|
||||
|
||||
## 9. Pre-archive 7-row checklist (AGENTS.md §13)
|
||||
|
||||
- [x] 9.1 No new generic types beyond `HashAlgorithm`/`HashAlgorithmRegistry` interfaces.
|
||||
- [x] 9.2 Zod schemas for `HashAlgorithm` shape (boundary validation when `register` is called).
|
||||
- [x] 9.3 Both new error codes (`CRYPTO_HASH_ALGORITHM_UNKNOWN`, `CRYPTO_SCHEMA_FINGERPRINT_MISMATCH`) thrown by ≥ 1 production code path.
|
||||
- [x] 9.4 Strict-mode behaviors: register-replace, registry-isolation tested.
|
||||
- [x] 9.5 OTel span `bitpacker-crypto.hash-registry.replaced` and `bitpacker-crypto.fingerprint` emitted from production paths; asserted with real in-memory `MeterProvider`.
|
||||
- [x] 9.6 Singleton lifecycle: `defaultHashRegistry()` returns the same reference on every call; `createHashRegistry()` returns a fresh one.
|
||||
- [x] 9.7 No retroactive tick.
|
||||
|
||||
## 10. Archive
|
||||
|
||||
- [x] 10.1 Fold `specs/bitpacker-crypto/spec.md` into `openspec/specs/bitpacker-crypto/spec.md` (creating the file).
|
||||
- [x] 10.2 Move `openspec/changes/bitpacker-crypto-rewrite/` → `openspec/archive/<yyyy-mm-dd>-bitpacker-crypto-rewrite/`.
|
||||
- [x] 10.3 Move `openspec/changes/bitpacker-crypto-port/` → `openspec/archive/<yyyy-mm-dd>-bitpacker-crypto-port/` (the original superseded predecessor).
|
||||
- [x] 10.4 STATUS.md update: bitpacker-crypto auxiliary primitives landed; full crypto/bitpacker consolidation lives in `restore-crypto-keychain-packer`.
|
||||
- [x] 10.5 Update `CLAUDE.md` package listing for `bitpacker-crypto` to describe `SchemaFingerprint`, `HashAlgorithmRegistry`, plus the existing checksum surface.
|
||||
|
||||
## 11. Notes for `restore-crypto-keychain-packer`
|
||||
|
||||
These items are NOT this change's scope but are recorded for the parallel work:
|
||||
|
||||
- The audit findings from this change's first draft (eight inline consumers manually re-implementing crypto+bitpacker patterns) feed into `restore-crypto-keychain-packer`'s consumer list. Those consumers will migrate to its `Packer` / `Authority` / `AuditLog` primitives in follow-up changes.
|
||||
- `MultiSignedBitSchema` (co-signed payloads — needed by `MeshTrustToken` NET-110 with `bothServerSignatures: [Uint8Array, Uint8Array]`) is NOT in `restore-crypto-keychain-packer`'s current spec. If/when needed, the spec delta lands there next to `Packer` and `RevocationList`, not in `bitpacker-crypto`.
|
||||
- The `bytes` field type and `byteAlign()` core-bitpacker additions in `restore-crypto-keychain-packer/specs/bitpacker/spec.md` are unaffected by this slim rewrite.
|
||||
Reference in New Issue
Block a user