28 KiB
@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—Encoderclass andencode(value, options?): Uint8Arrayfor RFC 8949 major types 0–7 with preferred-serialization shortest-form integers (§4.2.1). - ADDED:
src/encoder/encoder.types.ts—EncoderOptions,IndefiniteLengthMarker,PreferFloat16flag,MaxItems,MaxDepth. - ADDED:
src/encoder/indefinite.ts— opt-in indefinite-length encoder for arrays / maps / byte strings / text strings (§3.2). Disabled whendeterministic: true. - ADDED:
src/encoder/half-float.ts— IEEE-754 binary16 encode helper (major type 7 / value 25). Opt-in viaEncoderOptions.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./deterministicsubpath, layering reduction rules for numeric values and prohibition ofundefined/simple-valueover the deterministic baseline. - ADDED:
src/decoder/decoder.ts—Decoderclass anddecode(bytes, options?): unknownfor 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—PullDecoderoverUint8ArrayandAsyncIterable<Uint8Array>/ReadableStream<Uint8Array>exposingnext(): 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[], plusdecodeSequenceStreamasync iterator). - ADDED:
src/tags/registry.ts—TagRegistrywithregister(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): CoseKeyreturning the discriminated unionOkpKey | Ec2Key | RsaKey | SymmetricKeyper RFC 9052 §7. Exported under the./cosesubpath. - ADDED:
src/cose/jwk.ts—coseKeyToJwk(key): JsonWebKeyandjwkToCoseKey(jwk): CoseKeyround-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-curvealgorithm IDs. - ADDED:
src/cose/cose.types.ts—CoseKey,OkpKey,Ec2Key,RsaKey,SymmetricKey,CoseAlg,CoseKty,CoseCurve,CoseKeyOp. Discriminated bykty. - ADDED:
src/diagnostic/diagnostic.ts—toDiagnostic(value): stringemitting 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 withMALFORMED,UNEXPECTED_TYPE,UNKNOWN_TAG_STRICT,NESTED_TOO_DEEP,LENGTH_OVERFLOW,INDEFINITE_NOT_ALLOWED,NON_DETERMINISTICplusCborError extends SuaveplanError<CborErrorCode>. - ADDED:
src/observability/metrics.ts—cbor.encode_duration_ms+cbor.decode_duration_mshistograms;cbor.tag_total{tag}+cbor.error_total{code}counters. Wired into encode / decode hot paths. - ADDED:
src/schemas/schemas.ts— Zod boundary schemas forEncoderOptions,DecoderOptions,TagHandlerSchema,CoseKeySchemadiscriminated union,JwkSchema. Validated at every public entry point; failures surface asCBOR.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 (noexport *).
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) overAsyncIterable<Uint8Array>/ReadableStream<Uint8Array>../sequences— RFC 8742encodeSequence/decodeSequence/decodeSequenceStream../tags—TagRegistry,Tagged, built-in tag handlers../deterministic— RFC 8949 §4.2 deterministic profile + dCBOR profile../cose—COSE_Keyparsing,JWKround-trip, alg-map, plusCOSE_Sign/COSE_Sign1/COSE_Encrypt/COSE_Encrypt0/COSE_Mac/COSE_Mac0encode / 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—toDiagnosticdebug emitter../errors—CBOR_CODES,CborError,CborErrorCode.
Dependencies
@suaveplan/error workspace:^—defineCodes,SuaveplanErrorbase.@suaveplan/types workspace:^— shared interface contracts (Clockfor 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+./diagnosticsubpaths do NOT depend oncrypto-curveat 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 overUint8Array+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-ldpackage 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—CwtClaimsdiscriminated record with the seven IANA-registered claim labels (1iss, 2sub, 3aud, 4exp, 5nbf, 6iat, 7cti) plus aprivate[label]slot for unregistered labels; encoder acceptsDate | numberfor time-typed claims and round-trips throughTagged(1, epoch)per RFC 8392 §3. - ADDED:
src/cwt/encode.ts—encodeCwt(claims, options): Uint8Arrayproducing a deterministic-encoded claim set ready to be wrapped inCOSE_Sign1orCOSE_Mac0. - ADDED:
src/cwt/decode.ts—decodeCwt(bytes): CwtClaimsreturning the typed claim set; rejects malformed labels withCBOR.UNEXPECTED_TYPE. - ADDED:
src/cwt/verify.ts—verifyCwt(token, key, options): Promise<CwtVerifyResult>orchestratingCOSE_Sign1/COSE_Mac0verification via@suaveplan/crypto-curve(delegated through the COSE algorithm map) and time-window validation against an injectableClock. - ADDED:
src/cwt/cwt.types.ts—CwtClaims,CwtVerifyOptions,CwtVerifyResult,CwtPrivateClaim. - Errors used:
CBOR.UNEXPECTED_TYPE, newCBOR.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): Uint8Arrayper RFC 9052 §4.4. Five contexts:"Signature","Signature1","CounterSignature","MAC","MAC0". - ADDED:
src/cose/sign.ts—encodeCoseSign({ protected, unprotected, payload, signers }): Uint8ArrayanddecodeCoseSign(bytes): CoseSigncovering 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-curveECDSA / EdDSA verifiers. - ADDED:
src/cose/encrypt.ts—encodeCoseEncrypt({ protected, unprotected, ciphertext, recipients }): Uint8Array/decodeCoseEncrypt(bytes): CoseEncryptcovering 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): CoseMaccovering 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—CoseHeadermap 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): CddlAstproducing 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,CddlGenericParamdiscriminated unions. - ADDED:
src/cddl/validate.ts—validateCbor(value, schema, options): CddlValidationResultwalking 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 (.sizelength bound,.bitsset membership,.regexppattern match,.cbornested-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—ChunkDecoderfor incremental decode of one CBOR item larger than memory overAsyncIterable<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—ChunkDecoderEventdiscriminated union,ChunkDecoderOptions. - New error:
CBOR.STREAM_INTERRUPTEDfor 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 underdeterministic: 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; throwsNON_DETERMINISTICwhen caller forces an indefinite item.packages/vault/cbor/src/encoder/dcbor.test.ts— dCBOR reduction rules; rejectsundefined; 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—isWellFormedreturns 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 pernext(); bounded memory under a 1 GiB sequence (test asserts heap delta upper bound); cancels onsignal.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 returnsTagged.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;ktydiscriminator works.packages/vault/cbor/src/cose/jwk.test.ts— ES256 EC2 ↔ JWK round-trip; Ed25519 OKP ↔ JWK round-trip; RS256 RSA ↔ JWK round-trip (withn/ebase64url 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-curveID; unknown label throwsUNEXPECTED_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 withCOSE_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 withCOSE_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 withCOSE_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 throwsCWT_EXPIRED;nbffuture throwsCWT_NOT_YET_VALID; audience-mismatch throwsCWT_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 withCDDL_PARSE_FAILEDcarrying 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 withSTREAM_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-memoryMeterProvider;cbor.tag_total{tag}increments per decoded tag;cbor.error_total{code}increments on every thrownCborError.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: arbitraryJsonValue-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, perSTATUS.md).
No foundation-package fan-out applies (this is a new universal-tier leaf package; foundation deps @suaveplan/error and @suaveplan/types are unchanged).