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

5.9 KiB

EnvelopeSchema

A typed, version-aware envelope schema for bit-packed payloads. Wraps a caller-supplied BodyCodec with a [magic][formatVersion][body] frame so the wire format is self-describing on the magic + version dimensions, while the body remains pluggable. Replaces the manual defineBitSchema([{magic}, {formatVersion}, ...]) pattern that suaveplan crypto, packer, and other consumers had been re-authoring.

(no submodules)

Purpose

Many suaveplan wire formats begin with a magic number + format version: chain bundles (SVCB), public bundles, the chunked packer, and several network encoders all do the same thing manually. The duplication isn't just code churn — every callsite is a place where the magic/version contract can drift out of sync with its parser. EnvelopeSchema consolidates the pattern and makes "this byte string belongs to schema X version Y" a first-class runtime check.

The body region is opaque to the envelope itself; encoding is delegated to a BodyCodec<TBody> (pack + unpack). The bundled rawBytesCodec() is provided for callers whose bodies are already-encoded bytes (typical when composing with SignedBitSchema or EncryptedBitSchema); for structured bodies, callers pass a codec built from the legacy defineBitSchema(...) or from any encoder satisfying the two-method contract.

Features

  • pack(body) produces [magic][formatVersion][bodyCodec.pack(body)]
  • parse(bytes) extracts magic + version + raw body bytes WITHOUT decoding the body — enabling forward-compat dispatch when a future version arrives
  • parseStrict(bytes) validates magic + version, then decodes the body to TBody via bodyCodec.unpack
  • Mismatched magic throws BitpackerError with code CRYPTO_ENVELOPE_MAGIC_MISMATCH and context.{expected,actual}
  • Mismatched version throws BitpackerError with code CRYPTO_ENVELOPE_VERSION_MISMATCH and context.{expected,actual}
  • Zod-validated options at construction; unknown keys rejected
  • OTel spans bitpacker-crypto.envelope-pack and bitpacker-crypto.envelope-parse carry bitpacker.crypto.payload_bytes
  • Stays codec-agnostic so a future Protobuf-bodied envelope or a schema-fingerprinted variant requires zero changes to envelope itself

Usage: Basic Example

import {
    type BodyCodec,
    defineEnvelopeSchema,
} from "@suaveplan/bitpacker-crypto";
import { defineBitSchema } from "@suaveplan/bitpacker";

interface Header {
    foo: number;
    bar: number;
}

const headerSchema = defineBitSchema<Header>([
    { name: "foo", type: "uint", bits: 8 },
    { name: "bar", type: "uint", bits: 16 },
]);

const codec: BodyCodec<Header> = {
    pack: (body) => headerSchema.pack(body),
    unpack: (bytes) => headerSchema.unpack(bytes),
};

const envelope = defineEnvelopeSchema<Header>({
    magic: new Uint8Array([0x53, 0x56, 0x43, 0x42]), // "SVCB"
    formatVersion: 1,
    bodyCodec: codec,
});

const bytes = envelope.pack({ foo: 7, bar: 1234 });
const { foo, bar } = envelope.parseStrict(bytes); // typed: Header

Usage: Advanced Example — forward-compat dispatch

Use parse to peek at magic + version before deciding which decoder to run, so a v1 reader can detect v2 envelopes and route accordingly:

import { defineEnvelopeSchema } from "@suaveplan/bitpacker-crypto";

const envV1 = defineEnvelopeSchema({ magic, formatVersion: 1, bodyCodec: codecV1 });
const envV2 = defineEnvelopeSchema({ magic, formatVersion: 2, bodyCodec: codecV2 });

function decode(bytes: Uint8Array) {
    const partial = envV1.parse(bytes);
    if (partial.formatVersion === 1) return envV1.parseStrict(bytes);
    if (partial.formatVersion === 2) return envV2.parseStrict(bytes);
    throw new Error(`Unsupported envelope version: ${partial.formatVersion}`);
}

The body bytes are not consumed by parse, so the same input can be passed back to parseStrict of the matching version without re-allocation cost on the magic+version prefix.

API Reference

interface BodyCodec<TBody>

  • pack(body: TBody): Uint8Array — encode the body
  • unpack(bytes: Uint8Array): TBody — decode the body

interface EnvelopeSchemaOptions<TBody>

  • magic: Uint8Array — non-empty
  • formatVersion: number — integer 0..255
  • bodyCodec: BodyCodec<TBody> — body encoding hook

defineEnvelopeSchema<TBody>(options): EnvelopeSchema<TBody>

Returns:

  • magic: Uint8Array
  • formatVersion: number
  • pack(body: TBody): Uint8Array
  • parse(bytes: Uint8Array): { magic, formatVersion, bodyBytes }
  • parseStrict(bytes: Uint8Array): TBody
    • throws CRYPTO_ENVELOPE_MAGIC_MISMATCH on magic mismatch
    • throws CRYPTO_ENVELOPE_VERSION_MISMATCH on version mismatch

rawBytesCodec(): BodyCodec<Uint8Array>

Pass-through codec for bodies that are already-encoded raw bytes — useful when composing with SignedBitSchema or EncryptedBitSchema whose pack output goes inside an envelope.

Implementation Notes

  • The envelope wire format is [magic_bytes][1-byte u8 formatVersion][body_bytes]. The body is NOT framed with a length prefix — the codec is expected to either be fixed-size or to consume the entire trailing buffer.
  • parse throws CRYPTO_ENVELOPE_MAGIC_MISMATCH when input is shorter than magic.length + 1 (the minimum header size). Magic and version validation in parseStrict happens against the parsed values, so a too-short input is caught at the parse stage.
  • Magic comparison is constant-time-irrelevant — magic bytes are not secret. Equality is byte-wise. The error context includes hex-encoded expected and actual for diagnosability.
  • The codec is held by reference; mutating the codec after defineEnvelopeSchema(...) returns will affect future pack/parseStrict calls. Callers should treat the codec as immutable in practice.
  • OTel spans are emitted unconditionally (even when no SDK is wired) because they cost essentially nothing under the no-op tracer; production traces materialise once a tracer provider is installed.