# canonical ## Purpose Implements `canonicalSerialize`, the deterministic byte-level encoding that gates every server signature on a `DriveTemplate` (DTP-03). Two implementations of the function MUST produce byte-identical output for identical input regardless of in-memory key order. JSON is deliberately rejected because object-key ordering is not portable across runtimes. ## Features - Hand-rolled big-endian binary writer (`ByteWriter`) with `uint8` / `uint16` / `uint32` / `uint64` / `bool` / `string` / `bytes` primitives plus length-prefixed `optional*` helpers. - Fixed field order across the entire template tree — `schemaVersion`, `driveId`, `issuer`, `issuedAt`, `expiresAt`, `nonce`, `policy`, `cdnFallback`, `rootFolder`. - Recursive folder/file walker that tags each node with `TAG_FOLDER` (0x01) / `TAG_FILE` (0x02) so the decoder can reconstruct the tree without a separate manifest. - Encryption-aware: `inline` and `retroactive` `KeyDistribution` variants are tagged 1 / 2 respectively; the `wrappedKey` blob and `keyAuthority` string follow. - Excludes `serverSignature` — the bytes the issuer signs MUST not include the signature itself. ## Usage: Basic Example ```ts import { canonicalSerialize } from "@suaveplan/drive-template"; const bytes = canonicalSerialize({ schemaVersion: 1, driveId: "drive-1", rootFolder: { name: "/", children: [] }, policy: { defaultPolicy: { autoDownload: false, meshAllowed: true, cdnAllowed: true } }, issuer: "00".repeat(32), issuedAt: Date.now(), expiresAt: Date.now() + 60_000, nonce: new Uint8Array(16).fill(7), }); ``` ## Usage: Advanced Example ```ts import { canonicalSerialize, signTemplate } from "@suaveplan/drive-template"; // canonicalSerialize is the inputs-side; signTemplate runs Ed25519 over it. const tpl = signTemplate(buildTemplate(), serverSecretKey); const recanon = canonicalSerialize({ ...tpl, serverSignature: undefined as never }); console.log(`signed bytes: ${recanon.length}`); ``` ## API Reference - `canonicalSerialize(template: Omit): Uint8Array`. - Internal-only `ByteWriter` class with explicit primitives — not exported. ## Implementation Notes The `ByteWriter` accumulates `Uint8Array` parts and concatenates once at `finish()` to avoid quadratic growth. `optionalUint64` / `optionalUint32` / `optionalString` write a discriminator byte first so receivers know whether the field follows; this trades two bytes per optional for unambiguous parsing. The retention policy is encoded as a tag (`0` = unset, `1` = `permanent`, `2` = `lru`, `3` = `{ maxAgeMs }`) so future variants can extend the union without breaking older readers — they will see an unknown tag and reject the template through the schema-version pin.