Files
suaveplan/openspec/archive/2026-05-10-enterprise-cbor/proposal.md
2026-05-27 23:29:53 +01:00

28 KiB
Raw Blame History

@suaveplan/cbor — RFC 8949 CBOR encoder/decoder + RFC 9052 COSE bridge

Package: @suaveplan/cbor (NEW) Status: PROPOSED Type: new-package (genesis-original; greenfield — no upstream port) Date: 2026-05-10 Path: packages/vault/cbor/

Why

@suaveplan/webauthn parses CTAP2 authenticator data, attestation statements, and COSE_Key public keys (RFC 9052), every one of which is a CBOR-encoded payload. The package gates on a universal-tier CBOR codec. External alternatives (cbor-x, cbor) drag in CommonJS interop hazards, oversize transitive deps, and decode behaviors that diverge from RFC 8949 §5.1 strict well-formedness. An in-house, RFC-cited, zero-dependency codec keeps webauthn universal-tier and lets every future consumer (CWT per RFC 8392, dCBOR, COSE-keyed vault attachments) share one canonical implementation with one error registry, one OTel surface, and one deterministic-encoding profile.

What changes

New package: @suaveplan/cbor

  • ADDED: packages/vault/cbor/ package scaffold (package.json, tsconfig.lib.json, vite.config.ts, vitest.config.ts, README.md).
  • ADDED: src/encoder/encoder.ts — Encoder class and encode(value, options?): Uint8Array for RFC 8949 major types 0–7 with preferred-serialization shortest-form integers (§4.2.1).
  • ADDED: src/encoder/encoder.types.ts — EncoderOptions, IndefiniteLengthMarker, PreferFloat16 flag, MaxItems, MaxDepth.
  • ADDED: src/encoder/indefinite.ts — opt-in indefinite-length encoder for arrays / maps / byte strings / text strings (§3.2). Disabled when deterministic: true.
  • ADDED: src/encoder/half-float.ts — IEEE-754 binary16 encode helper (major type 7 / value 25). Opt-in via EncoderOptions.preferFloat16.
  • ADDED: src/encoder/deterministic.ts — RFC 8949 §4.2.1 + §4.2.2 deterministic profile: shortest-form integers, sorted map keys (lexicographic over canonical CBOR-encoded key bytes), no indefinite-length encodings, half-float disabled.
  • ADDED: src/encoder/dcbor.ts — dCBOR profile (draft-mcnally-deterministic-cbor) exported under the ./deterministic subpath, layering reduction rules for numeric values and prohibition of undefined / simple-value over the deterministic baseline.
  • ADDED: src/decoder/decoder.ts — Decoder class and decode(bytes, options?): unknown for RFC 8949 major types 0–7. Strict mode (default) enforces every §5.1 well-formedness rule.
  • ADDED: src/decoder/decoder.types.ts — DecoderOptions, Tagged<T>, DecoderMode = "strict" | "lax".
  • ADDED: src/decoder/well-formed.ts — well-formedness checker per §5.1 (isWellFormed(bytes): boolean, plus internal helpers used during decode).
  • ADDED: src/decoder/half-float.ts — IEEE-754 binary16 decode (parity with reference implementation; never gated, always available).
  • ADDED: src/streaming/pull-decoder.ts — PullDecoder over Uint8Array and AsyncIterable<Uint8Array> / ReadableStream<Uint8Array> exposing next(): Promise<IteratorResult<unknown>> so callers materialize one item at a time. Bounded-memory; never buffers the whole stream.
  • ADDED: src/sequences/sequences.ts — RFC 8742 CBOR Sequences encode (encodeSequence(items, options?): Uint8Array) and decode (decodeSequence(bytes, options?): unknown[], plus decodeSequenceStream async iterator).
  • ADDED: src/tags/registry.ts — TagRegistry with register(tag, handler) / unregister(tag) / lookup(tag). Built-in tags: 0 (RFC 3339 date/time), 1 (epoch), 2 (positive bignum), 3 (negative bignum), 21 (base64url), 22 (base64), 23 (base16), 24 (encoded CBOR), 32 (URI), 35 (regex), 55799 (self-describe).
  • ADDED: src/tags/tagged.ts — Tagged<T> value object ({ tag: number, value: T }) returned by the decoder for unknown tags in strict mode-with-fallthrough and in lax mode.
  • ADDED: src/cose/cose-key.ts — decodeCoseKey(bytes): CoseKey returning the discriminated union OkpKey | Ec2Key | RsaKey | SymmetricKey per RFC 9052 §7. Exported under the ./cose subpath.
  • ADDED: src/cose/jwk.ts — coseKeyToJwk(key): JsonWebKey and jwkToCoseKey(jwk): CoseKey round-trip helpers.
  • ADDED: src/cose/alg-map.ts — coseAlgToCryptoAlg(alg) mapping COSE algorithm labels (-7 ES256, -8 EdDSA, -35 ES384, -36 ES512, -257 RS256, etc.) to genesis-internal @suaveplan/crypto-curve algorithm IDs.
  • ADDED: src/cose/cose.types.ts — CoseKey, OkpKey, Ec2Key, RsaKey, SymmetricKey, CoseAlg, CoseKty, CoseCurve, CoseKeyOp. Discriminated by kty.
  • ADDED: src/diagnostic/diagnostic.ts — toDiagnostic(value): string emitting RFC 8949 §8 diagnostic notation. Marked debug-only in docs; never invoked by encoder / decoder hot paths.
  • ADDED: src/errors/error-codes.ts — defineCodes("CBOR", [...]) registry with MALFORMED, UNEXPECTED_TYPE, UNKNOWN_TAG_STRICT, NESTED_TOO_DEEP, LENGTH_OVERFLOW, INDEFINITE_NOT_ALLOWED, NON_DETERMINISTIC plus CborError extends SuaveplanError<CborErrorCode>.
  • ADDED: src/observability/metrics.ts — cbor.encode_duration_ms + cbor.decode_duration_ms histograms; cbor.tag_total{tag} + cbor.error_total{code} counters. Wired into encode / decode hot paths.
  • ADDED: src/schemas/schemas.ts — Zod boundary schemas for EncoderOptions, DecoderOptions, TagHandlerSchema, CoseKeySchema discriminated union, JwkSchema. Validated at every public entry point; failures surface as CBOR.MALFORMED (decoder input) or as a Zod-wrapped boundary error (option misuse).
  • ADDED: src/__benchmarks__/encode.bench.ts, decode.bench.ts, cose-key.bench.ts — mitata benchmarks for 1 KiB COSE_Key, 1 MiB nested map, 100 KiB array of small items.
  • ADDED: src/index.ts — explicit named re-exports of the public surface (no export *).

Subpath exports

  • . — root barrel: encode, decode, Tagged, TagRegistry, error codes, OTel.
  • ./encoder — Encoder, encode, EncoderOptions, half-float helper.
  • ./decoder — Decoder, decode, DecoderOptions, well-formedness checker.
  • ./streaming — PullDecoder (whole-item) + ChunkDecoder (partial-item) over AsyncIterable<Uint8Array> / ReadableStream<Uint8Array>.
  • ./sequences — RFC 8742 encodeSequence / decodeSequence / decodeSequenceStream.
  • ./tags — TagRegistry, Tagged, built-in tag handlers.
  • ./deterministic — RFC 8949 §4.2 deterministic profile + dCBOR profile.
  • ./cose — COSE_Key parsing, JWK round-trip, alg-map, plus COSE_Sign / COSE_Sign1 / COSE_Encrypt / COSE_Encrypt0 / COSE_Mac / COSE_Mac0 encode / decode / verify / decrypt (RFC 9052 §4–§6).
  • ./cwt — RFC 8392 CBOR Web Token claim encode / decode / verify with time-window validation.
  • ./cddl — RFC 8610 CDDL lexer / parser / validator.
  • ./diagnostic — toDiagnostic debug emitter.
  • ./errors — CBOR_CODES, CborError, CborErrorCode.

Dependencies

  • @suaveplan/error workspace:^ — defineCodes, SuaveplanError base.
  • @suaveplan/types workspace:^ — shared interface contracts (Clock for CWT time validation, base interfaces).
  • @suaveplan/crypto-curve workspace:^ — only consumed by ./cose (coseAlgToCryptoAlg, verifyCoseSign1) and ./cwt (verifyCwt). Root + ./encoder + ./decoder + ./cddl + ./streaming + ./sequences + ./tags + ./diagnostic subpaths do NOT depend on crypto-curve at runtime.
  • @suaveplan/crypto-cipher workspace:^ — only consumed by ./cose (decryptCoseEncrypt0) for AES-GCM / ChaCha20-Poly1305 AEAD primitives.
  • @suaveplan/crypto-hash workspace:^ — only consumed by ./cose (verifyCoseMac0) for HMAC-SHA-{256,384,512}.
  • @opentelemetry/api catalog:observability — encode/decode histograms, tag/error counters, COSE / CWT / CDDL operation spans.
  • tslib catalog:build.
  • zod catalog:forms — option boundary validation.
  • No @noble/*, cbor-x, cbor, @stablelib/*, browser globals, or Node built-ins. Pure TypeScript over Uint8Array + DataView.

Out of scope (residual)

Per maintainer instruction, every previously-deferred capability has been folded back IN-scope (CWT, full COSE Sign/Sign1/Encrypt/Encrypt0/Mac/Mac0, CDDL parser+validator, partial-item streaming). The only remaining residual exclusion:

  • CBOR-LD / SD-CBOR-LD — distinct semantic-web layer (RDF + JSON-LD context compression) above CBOR; addressed in a separate @suaveplan/cbor-ld package when a consumer requests it. Justification: CBOR-LD requires a JSON-LD context resolver and an RDF graph model neither of which are CBOR-codec concerns; folding it in would couple this package to a semantic-web stack none of the current or pending consumers need. Not a silent gap.

In-scope additions (Wave 2 — folded back per maintainer)

CWT (RFC 8392) — ./cwt subpath

  • ADDED: src/cwt/claims.ts — CwtClaims discriminated record with the seven IANA-registered claim labels (1 iss, 2 sub, 3 aud, 4 exp, 5 nbf, 6 iat, 7 cti) plus a private[label] slot for unregistered labels; encoder accepts Date | number for time-typed claims and round-trips through Tagged(1, epoch) per RFC 8392 §3.
  • ADDED: src/cwt/encode.ts — encodeCwt(claims, options): Uint8Array producing a deterministic-encoded claim set ready to be wrapped in COSE_Sign1 or COSE_Mac0.
  • ADDED: src/cwt/decode.ts — decodeCwt(bytes): CwtClaims returning the typed claim set; rejects malformed labels with CBOR.UNEXPECTED_TYPE.
  • ADDED: src/cwt/verify.ts — verifyCwt(token, key, options): Promise<CwtVerifyResult> orchestrating COSE_Sign1 / COSE_Mac0 verification via @suaveplan/crypto-curve (delegated through the COSE algorithm map) and time-window validation against an injectable Clock.
  • ADDED: src/cwt/cwt.types.ts — CwtClaims, CwtVerifyOptions, CwtVerifyResult, CwtPrivateClaim.
  • Errors used: CBOR.UNEXPECTED_TYPE, new CBOR.CWT_EXPIRED, CBOR.CWT_NOT_YET_VALID, CBOR.CWT_AUDIENCE_MISMATCH, CBOR.CWT_ISSUER_MISMATCH, CBOR.CWT_SIGNATURE_INVALID.

COSE structures (RFC 9052 §4–§6) — extend ./cose subpath

  • ADDED: src/cose/sig-structure.ts — buildSigStructure(context, protected, externalAad, payload): Uint8Array per RFC 9052 §4.4. Five contexts: "Signature", "Signature1", "CounterSignature", "MAC", "MAC0".
  • ADDED: src/cose/sign.ts — encodeCoseSign({ protected, unprotected, payload, signers }): Uint8Array and decodeCoseSign(bytes): CoseSign covering RFC 9052 §4.1 multi-signer messages (CBOR tag 98).
  • ADDED: src/cose/sign1.ts — encodeCoseSign1({ protected, unprotected, payload, signature }): Uint8Array / decodeCoseSign1(bytes): CoseSign1 / verifyCoseSign1(msg, key, options): Promise<boolean> covering RFC 9052 §4.2 single-signer messages (CBOR tag 18). Wires the algorithm-map → @suaveplan/crypto-curve ECDSA / EdDSA verifiers.
  • ADDED: src/cose/encrypt.ts — encodeCoseEncrypt({ protected, unprotected, ciphertext, recipients }): Uint8Array / decodeCoseEncrypt(bytes): CoseEncrypt covering RFC 9052 §5.1 multi-recipient encrypted messages (CBOR tag 96).
  • ADDED: src/cose/encrypt0.ts — encodeCoseEncrypt0({ protected, unprotected, ciphertext }): Uint8Array / decodeCoseEncrypt0(bytes): CoseEncrypt0 / decryptCoseEncrypt0(msg, key, options): Promise<Uint8Array> covering RFC 9052 §5.2 single-recipient encrypted messages (CBOR tag 16). Wires AES-GCM / ChaCha20-Poly1305 via @suaveplan/crypto-cipher.
  • ADDED: src/cose/mac.ts — encodeCoseMac({ protected, unprotected, payload, tag, recipients }): Uint8Array / decodeCoseMac(bytes): CoseMac covering RFC 9052 §6.1 multi-recipient MAC messages (CBOR tag 97).
  • ADDED: src/cose/mac0.ts — encodeCoseMac0({ protected, unprotected, payload, tag }): Uint8Array / decodeCoseMac0(bytes): CoseMac0 / verifyCoseMac0(msg, key, options): Promise<boolean> covering RFC 9052 §6.2 single-recipient MAC messages (CBOR tag 17). Wires HMAC-SHA-{256,384,512} via @suaveplan/crypto-hash.
  • ADDED: src/cose/headers.ts — CoseHeader map type, header-label registry covering RFC 9052 §3.1 (alg, crit, content type, kid, IV, Partial IV, counter signature).
  • New errors: CBOR.COSE_SIG_INVALID, CBOR.COSE_AEAD_FAILED, CBOR.COSE_HEADER_INVALID, CBOR.COSE_RECIPIENT_NOT_FOUND.

CDDL (RFC 8610) parser + validator — ./cddl subpath

  • ADDED: src/cddl/lexer.ts — RFC 8610 ABNF token stream (identifier, =, /, //, *, +, ?, .., &, ~, control operators .size .bits .regexp .cbor .within .and .lt .le .gt .ge .eq .ne .default, integer / text-string / byte-string / float literals, comments).
  • ADDED: src/cddl/parser.ts — parseCddl(source): CddlAst producing the typed AST per RFC 8610 §3 grammar (rules, types, groups, occurrence indicators, ranges, choices, control operators, generic parameters).
  • ADDED: src/cddl/ast.ts — CddlAst, CddlRule, CddlType, CddlGroup, CddlOccurrence, CddlChoice, CddlControl, CddlGenericParam discriminated unions.
  • ADDED: src/cddl/validate.ts — validateCbor(value, schema, options): CddlValidationResult walking a decoded CBOR value against a CDDL rule by name; emits a structured error path on mismatch ({ path: (string | number)[], expected, got }).
  • ADDED: src/cddl/built-ins.ts — RFC 8610 §3.8 prelude (uint, nint, int, bstr, tstr, bool, nil, null, undefined, tdate, time, number, text, bytes, float, float16, float32, float64, any).
  • ADDED: src/cddl/control-ops.ts — control-operator implementations (.size length bound, .bits set membership, .regexp pattern match, .cbor nested-CBOR bytes, .within, .and, comparison ops, .default).
  • ADDED: src/cddl/cddl.types.ts — option / result types, error path discriminator.
  • New errors: CBOR.CDDL_PARSE_FAILED, CBOR.CDDL_VALIDATION_FAILED, CBOR.CDDL_RULE_NOT_FOUND, CBOR.CDDL_UNDEFINED_REFERENCE.

Partial-item streaming — extend ./streaming subpath

  • ADDED: src/streaming/chunk-decoder.ts — ChunkDecoder for incremental decode of one CBOR item larger than memory over AsyncIterable<Uint8Array> / ReadableStream<Uint8Array>. Emits { kind: "string-chunk" | "bytes-chunk" | "array-start" | "array-end" | "map-start" | "map-end" | "tag" | "value" | "break" } events as the underlying bytes flow in. Bounded memory regardless of item size.
  • ADDED: src/streaming/chunk-decoder.types.ts — ChunkDecoderEvent discriminated union, ChunkDecoderOptions.
  • New error: CBOR.STREAM_INTERRUPTED for mid-item disconnect.

Source mapping

This package is genesis-original; no ../suaveplan/enterprise/cbor.md line range exists for any feature. Per maintainer instruction the source-of-record is the RFC itself.

Requirement RFC source Notes
Encoder for major types 0–7 with preferred-serialization shortest-form integers RFC 8949 §3.1 + §4.2.1 unsigned, negative, byte-string, text-string, array, map, tag, simple/float
Indefinite-length arrays / maps / strings (opt-in) RFC 8949 §3.2 Disallowed when deterministic: true; throws INDEFINITE_NOT_ALLOWED
Tagged items + built-in tag registry (0, 1, 2, 3, 21, 22, 23, 24, 32, 35, 55799) RFC 8949 §3.4 + IANA CBOR Tags Registry Each tag's semantics cited in sibling .md; tag 24 (encoded CBOR) re-enters the decoder
Half-precision float encode (opt-in) + decode RFC 8949 §3.3 (Major 7 / value 25) + §D Binary16 IEEE-754; reference test vectors in §A
Preferred serialization (shortest-form ints, no indefinite) RFC 8949 §4.1 + §4.2.1 Always-on; deterministic profile is a strict superset
Deterministic encoding profile RFC 8949 §4.2.1 + §4.2.2 Sorted map keys via lexicographic comparison over canonical encoded key bytes
dCBOR profile draft-mcnally-deterministic-cbor (latest) §3 Reduction rules for numeric values; prohibits undefined / generic simple values
RFC 8742 CBOR Sequences encode + decode RFC 8742 §2 + §3 Concatenation of zero-or-more well-formed CBOR items; no enclosing array
Decoder strict-mode well-formedness RFC 8949 §5.1 Includes "no extraneous bytes after item" (§5.1.2) and "all break stops have a matching indefinite start"
Decoder lax mode RFC 8949 §5.2 (basic validity) Lax mode applies §5.2 only; strict mode adds §5.1
Streaming pull-decoder RFC 8949 §3.2 + §5.1 Bounded memory; consumer drives via next()
Bounded recursion + bounded item count RFC 8949 §1.2 (security considerations) + §10 maxDepth: 64 default → NESTED_TOO_DEEP; maxItems: 1_000_000 default → LENGTH_OVERFLOW
decodeCoseKey returning typed key union RFC 9052 §7 + IANA COSE Key Common Parameters Discriminator on kty (label 1)
coseKeyToJwk / jwkToCoseKey round-trip RFC 9052 §7.1 + RFC 8037 §2 + RFC 7518 §6 EC2/OKP/RSA/symmetric round-trip
COSE alg label → genesis algorithm ID RFC 9053 §2 + IANA COSE Algorithms Registry -7 ES256, -8 EdDSA, -35 ES384, -36 ES512, -257 RS256, …; rejects unknown labels
Sig_structure / Enc_structure / MAC_structure builders RFC 9052 §4.4 + §5.3 + §6.3 Five contexts: Signature, Signature1, CounterSignature, MAC, MAC0; deterministic-encoded
COSE_Sign / COSE_Sign1 encode + decode + verify RFC 9052 §4.1 + §4.2 + IANA CBOR Tags 98 / 18 Wires ECDSA / EdDSA via @suaveplan/crypto-curve
COSE_Encrypt / COSE_Encrypt0 encode + decode + decrypt RFC 9052 §5.1 + §5.2 + IANA CBOR Tags 96 / 16 Wires AES-GCM / ChaCha20-Poly1305 via @suaveplan/crypto-cipher
COSE_Mac / COSE_Mac0 encode + decode + verify RFC 9052 §6.1 + §6.2 + IANA CBOR Tags 97 / 17 Wires HMAC-SHA-{256,384,512} via @suaveplan/crypto-hash
COSE header registry (alg, crit, content type, kid, IV, Partial IV, counter signature) RFC 9052 §3.1 + IANA COSE Header Parameters Typed CoseHeader map; rejects malformed labels
CWT claim labels (iss/sub/aud/exp/nbf/iat/cti) RFC 8392 §3 + IANA CWT Claims Typed CwtClaims discriminator; epoch tag round-trip
CWT verify (signature + time window) RFC 8392 §7 + RFC 9052 §4.2 / §6.2 Delegates to verifyCoseSign1 / verifyCoseMac0; Clock injected
CDDL lexer + parser RFC 8610 §3 ABNF grammar Full prelude; control operators; generic params; comments
CDDL validator with structured error path RFC 8610 §3 + §3.8 prelude { path, expected, got } discriminator on mismatch
CDDL control operators RFC 8610 §3.8.1–3.8.16 .size, .bits, .regexp, .cbor, .within, .and, comparisons, .default
Partial-item streaming (ChunkDecoder) RFC 8949 §3.2 (indefinite-length forms) Event stream of string-chunk / bytes-chunk / container-start/end / break; bounded memory regardless of item size
Diagnostic notation RFC 8949 §8 Debug-only emitter; never on hot path

Requirements with no source line

Requirement Justification
defineCodes("CBOR", [...]) 7-code registry Genesis convention — every package surfaces typed errors via @suaveplan/error. The seven codes (MALFORMED, UNEXPECTED_TYPE, UNKNOWN_TAG_STRICT, NESTED_TOO_DEEP, LENGTH_OVERFLOW, INDEFINITE_NOT_ALLOWED, NON_DETERMINISTIC) each map to a distinct §5.1 / §10 failure class so callers can branch on root cause.
Zod boundary schemas at every public option type Genesis convention (CLAUDE.md §2 / §11). Catches caller misuse at the boundary; option misuse never silently degrades into malformed bytes.
OTel surface (cbor.encode_duration_ms, cbor.decode_duration_ms, cbor.tag_total, cbor.error_total) Genesis convention — every hot-path package emits histograms + counters. Tag attribute is bounded by IANA registry size; error code attribute is bounded by the 7-entry code registry.
300-line file budget + 100/100/100/100 coverage + sibling .md per impl file CLAUDE.md global rules (Quality + Documentation sections).
Property-based round-trip tests (fast-check) + deterministic-mode commutativity CLAUDE.md "no mocks" rule; round-trip is the only test that proves encoder + decoder agree on the wire format under arbitrary structures.
Mitata benchmarks under src/__benchmarks__/ CLAUDE.md global benchmark requirement. Baseline numbers feed the README perf table and a future CI regression gate.
Tag-handler registry pluggability Future consumer extensibility — webauthn may register WebAuthn-specific tags (e.g. AAGUID handling) without modifying this package.

Verification approach

Each requirement carries a **Verified by:** line in the spec delta naming concrete it(...) titles inside the test files below.

  • packages/vault/cbor/src/encoder/encoder.test.ts — major types 0–7 encode, preferred-serialization shortest-form integers, simple-value encoding, float32 / float64 encode.
  • packages/vault/cbor/src/encoder/encoder.spec-conformance.test.ts — RFC 8949 Appendix A test vectors (every published vector produces the byte sequence in the appendix).
  • packages/vault/cbor/src/encoder/indefinite.test.ts — opt-in indefinite-length emission for arrays / maps / strings; rejected under deterministic: true.
  • packages/vault/cbor/src/encoder/half-float.test.ts — binary16 encode for normal, subnormal, ±0, ±Inf, NaN, smallest-normal, largest-normal.
  • packages/vault/cbor/src/encoder/deterministic.test.ts — sorted map keys; shortest-form ints; no indefinite-length output; throws NON_DETERMINISTIC when caller forces an indefinite item.
  • packages/vault/cbor/src/encoder/dcbor.test.ts — dCBOR reduction rules; rejects undefined; property-based commutativity.
  • packages/vault/cbor/src/decoder/decoder.test.ts — major types 0–7 decode; tagged-value materialization; Tagged<T> for unknown tags; strict-mode rejects extraneous trailing bytes.
  • packages/vault/cbor/src/decoder/decoder.spec-conformance.test.ts — RFC 8949 Appendix A round-trip; §5.1 well-formedness rules; lax vs strict.
  • packages/vault/cbor/src/decoder/well-formed.test.ts — isWellFormed returns false for every §5.1 failure class.
  • packages/vault/cbor/src/decoder/half-float.test.ts — binary16 decode parity with reference encoder; subnormals; payload-preserving NaN.
  • packages/vault/cbor/src/streaming/pull-decoder.test.ts — pulls one item per next(); bounded memory under a 1 GiB sequence (test asserts heap delta upper bound); cancels on signal.aborted.
  • packages/vault/cbor/src/sequences/sequences.test.ts — RFC 8742 encode + decode; empty sequence; mixed-major-type sequence; partial-trailing-byte rejection in strict mode.
  • packages/vault/cbor/src/tags/registry.test.ts — register / lookup / unregister; double-register rejects; unknown tag in strict-fallthrough returns Tagged.
  • packages/vault/cbor/src/tags/builtin.test.ts — every built-in tag (0, 1, 2, 3, 21, 22, 23, 24, 32, 35, 55799) round-trips through the registry.
  • packages/vault/cbor/src/cose/cose-key.test.ts — RFC 9052 §7 sample EC2, OKP, RSA, symmetric keys decode to the typed union; kty discriminator works.
  • packages/vault/cbor/src/cose/jwk.test.ts — ES256 EC2 ↔ JWK round-trip; Ed25519 OKP ↔ JWK round-trip; RS256 RSA ↔ JWK round-trip (with n/e base64url encoding); symmetric key round-trip.
  • packages/vault/cbor/src/cose/alg-map.test.ts — every IANA-registered alg label this package supports maps to the right @suaveplan/crypto-curve ID; unknown label throws UNEXPECTED_TYPE.
  • packages/vault/cbor/src/cose/sig-structure.test.ts — RFC 9052 §4.4 vectors for all five contexts produce byte-identical canonical output.
  • packages/vault/cbor/src/cose/sign1.test.ts — RFC 9052 Appendix C.2 ES256 / EdDSA test vectors round-trip; tampered signature rejected with COSE_SIG_INVALID; tag-prefix and tagless inputs both supported.
  • packages/vault/cbor/src/cose/sign.test.ts — multi-signer encode + decode; per-signer header preserved; verify-any returns the matching signer index.
  • packages/vault/cbor/src/cose/encrypt0.test.ts — RFC 9052 Appendix C.3 AES-GCM-256 vector decrypts; tampered ciphertext rejected with COSE_AEAD_FAILED; ChaCha20-Poly1305 round-trip.
  • packages/vault/cbor/src/cose/encrypt.test.ts — multi-recipient encode + decode; per-recipient KEK lookup.
  • packages/vault/cbor/src/cose/mac0.test.ts — HMAC-SHA-256 / 384 / 512 round-trip; tampered MAC rejected.
  • packages/vault/cbor/src/cose/mac.test.ts — multi-recipient MAC round-trip.
  • packages/vault/cbor/src/cose/headers.test.ts — header label registry round-trip; malformed label rejected with COSE_HEADER_INVALID.
  • packages/vault/cbor/src/cwt/claims.test.ts — every IANA claim label round-trips; Date ↔ epoch via tag 1; private claims preserved.
  • packages/vault/cbor/src/cwt/encode.test.ts — deterministic claim-set encoding; canonical bytes stable across two runs.
  • packages/vault/cbor/src/cwt/decode.test.ts — RFC 8392 Appendix A vectors decode to typed claims.
  • packages/vault/cbor/src/cwt/verify.test.ts — COSE_Sign1-wrapped CWT verifies; expired token throws CWT_EXPIRED; nbf future throws CWT_NOT_YET_VALID; audience-mismatch throws CWT_AUDIENCE_MISMATCH.
  • packages/vault/cbor/src/cddl/lexer.test.ts — RFC 8610 §3 token-stream coverage including comments, control operators, ranges.
  • packages/vault/cbor/src/cddl/parser.test.ts — every grammar production from RFC 8610 §3; rejects malformed source with CDDL_PARSE_FAILED carrying line / column.
  • packages/vault/cbor/src/cddl/validate.test.ts — RFC 8610 Appendix examples (CWT schema, COSE_Sign schema) validate against real RFC vectors; mismatch surfaces { path, expected, got }.
  • packages/vault/cbor/src/cddl/built-ins.test.ts — every prelude type accepts its conforming values and rejects non-conforming.
  • packages/vault/cbor/src/cddl/control-ops.test.ts — every control operator (.size, .bits, .regexp, .cbor, .within, .and, comparisons, .default) tested with conforming + non-conforming input.
  • packages/vault/cbor/src/streaming/chunk-decoder.test.ts — emits expected event sequence for an indefinite-length text-string in 3 chunks; bounded heap delta over a 10 GiB synthetic indefinite byte-string; aborts mid-item with STREAM_INTERRUPTED.
  • packages/vault/cbor/src/diagnostic/diagnostic.test.ts — RFC 8949 §8 sample diagnostic strings emit byte-identical output.
  • packages/vault/cbor/src/errors/error-codes.test.ts — every code in the registry is thrown by ≥1 production code path; expectTypeOf<CborErrorCode>().toEqualTypeOf<…>() pins the literal union.
  • packages/vault/cbor/src/observability/metrics.test.ts — encode/decode histograms record under a real in-memory MeterProvider; cbor.tag_total{tag} increments per decoded tag; cbor.error_total{code} increments on every thrown CborError.
  • packages/vault/cbor/src/schemas/schemas.test.ts — every Zod schema rejects malformed options with the genesis boundary error.
  • packages/vault/cbor/src/encoder/property.test.ts — fast-check round-trip: arbitrary JsonValue-like structures encode → decode → byte-identical.
  • packages/vault/cbor/src/encoder/deterministic-property.test.ts — fast-check: deterministic encoder emits byte-identical output across two runs on shuffled-key maps.
  • packages/vault/cbor/src/index.test.ts — every documented subpath export resolves.

Migration impact

None — additive only; this is the first delta of a new package. No existing code in genesis imports from @suaveplan/cbor. @suaveplan/webauthn is the gating consumer and lands in a follow-up change once this package archives; that change will add the dep edge and migrate webauthn's CTAP2 / attestation parsers off any external CBOR placeholder.

Impact

Affected dependents

bunx turbo ls --filter=...@suaveplan/cbor returns no current dependents (the package does not yet exist on genesis). The pending consumer is:

  • @suaveplan/webauthn — [ ] tests run / [ ] docs reviewed / [x] no action needed in THIS change (reason: webauthn is itself pending migration; consumer wiring lands in the webauthn migration change once that proposal is opened, per STATUS.md).

No foundation-package fan-out applies (this is a new universal-tier leaf package; foundation deps @suaveplan/error and @suaveplan/types are unchanged).