# CANONICALIZATION-LAW-1 — v2

Word Of Clout, LLC · published under the one-ballot one-push publish (Tani ruling 2026-09-21T15:21Z msg 30772) · supersedes the v1 text formerly at this stable path · stable path: /laws/canonicalization-v1.md · sha256 (this file's bytes): see the parity receipt — LAW-SHA256 quoted there.

A published record's signature can only be checked against a specific
byte sequence. This document is that recipe. A verifier who follows it
exactly, on the record's own published bytes, will hash the same bytes
the signer hashed.

**LAW-IS-GENERAL-1 (Tani 2026-09-21T06:31Z msg 30758):** a law states
rules, never instances. The v1 text named specific record generations,
their canonical-form digests, a receipt archive path, and an issuer key
id in the Eras section; all of those are instances and are REMOVED from
the law. They live where instances live — the reach gate (which verifies
every live generation, all eras, on every build, against public bytes)
and the receipts.

## The Rule

**Canonical form: recursively key-sorted, no whitespace, JSON.**

1. Take the published record. Exclude three fields entirely:
   `signature`, `signing`, and `derivation`. Every other field is in the
   canonical form, including `public_key` and `dependency_manifest_sha256`.
2. Canonicalize by this recipe:
   - Objects: each object's keys sorted in ascending lexicographic order
     (the order of `Array.prototype.sort` on the UTF-16 code units of the
     key strings), recursively; an own `__proto__` key, if present, is an
     ordinary own key — the accumulator has no prototype, so no key can
     silently vanish or invoke an inherited setter.
   - Arrays: order preserved as-is, elements canonicalized recursively.
   - Everything else (strings, numbers, `true`, `false`, `null`):
     unchanged, encoded as standard JSON.
   - Whitespace: none. No spaces, newlines, or padding anywhere between
     tokens.
3. The result is UTF-8 encoded; its SHA-256 digest is the value that the
   Ed25519 signature covers. The public key is the record's own embedded
   `public_key` (SubjectPublicKeyInfo, DER encoding).
4. The `dependency_manifest_sha256` inside a record is computed by the same
   recipe — recursively key-sorted, no whitespace — over the
   `dependency_manifest` object alone (the manifest's stored key order
   never matters).

A verifier reproduces step 2 on the record bytes as served, hashes with
SHA-256, and checks the Ed25519 signature against the embedded public key.
This is the same single implementation the signer uses; the source is
open, and this document is its published contract.

## Eras (CANON-ERA-1)

**The era is selected by an explicit signed field — never by sniffing
shape, and never by any field outside the signed scope.**

- **Era selection rule:** if the record carries `dependency_manifest`
  with a `ruleset_version`, that value names the record's era. If
  `dependency_manifest` is absent — or present without a non-empty
  `ruleset_version` — the record is era v1. `dependency_manifest` is a
  signed field, so the era choice is itself covered by the signature —
  a verifier never guesses it, and a field outside the signed scope can
  never select it.
- **The selector reads ONLY signed scope.** The signature check is
  era-independent and VALID for every generation of every era: the
  signature covers the canonical form (steps 1-3), which excludes the
  signing block and the derivation block, so metadata in those blocks
  can never change the recipe a record verifies under. A record whose
  signing block is mutated or removed selects the SAME era and verifies
  under the SAME recipe.
- **Manifest-sha computation.** Step 4 above (the key-sorted recipe —
  "the manifest's stored key order never matters") is the rule of the
  current publisher, and the recipe every re-derivation applies first. A
  generation whose signed `dependency_manifest_sha256` value does not
  re-derive under step 4 carries a NAMED mismatch — it is never a silent
  pass and never a silent failure. One historical class exists: a
  generation signed before the publisher adopted step 4 whose value
  re-derives under the manifest's stored key order. That class is
  inventoried and pinned by the reach gate, which re-derives every live
  generation — all eras, both computations — against public bytes on
  every build; the gate's pins are instances and live in the gate and
  its receipts, not in this law. If the ruleset recipe ever changes
  again, the new era is added here, selected by the same signed field,
  and the ruleset version is BUMPED at the change — a recipe change
  without a version bump is a publisher drift the gate detects.
- **A third party repeating the check** on a record's own published
  bytes selects the era and the recipe by the record's SIGNED fields
  alone — the ruleset version and, for the manifest-sha leg, the signed
  digest value — and reports a named mismatch for anything that does
  not re-derive under a permitted computation. No unsigned metadata
  (issuance timestamps in the signing block, fetch times, page context)
  participates in selection.

## The limit, stated plainly

This recipe defines **how to check a record's signature** — nothing
else. It does not describe where keys are stored, how records are
produced, or how the signer is operated; those are not published here
and never appear in record bytes. The signature covers the fields named
in step 1; the `derivation` block is deliberately excluded from signing
(its content is bound instead through the signed
`dependency_manifest_sha256`). Any claim of validity on bytes other
than the record's own canonical form, or against a key other than the
embedded one, is outside this law.

## Parity

CANON-ERA-PARITY-1: the era selector reads ONLY fields inside the signed
scope — mutating the signing block's timestamp to any value, or deleting
the signing block entirely, changes NEITHER the selected era NOR the
canonical bytes the signature covers; a record with no
`dependency_manifest` is era v1; a record whose `dependency_manifest`
carries an empty `ruleset_version` is era v1 (the field, not the shape,
selects). The historical stored-key-order class is detected and NAMED,
never silently passed. The law carries no instances (LAW-IS-GENERAL-1):
no record ids, no digest values, no archive paths — the per-generation
evidence lives in the reach gate and its receipts. Parity runner:
`node lib/records/r14-3-canonicalization-v2-parity.test.mjs` — exit code
quoted in the commit receipt. The in-process reach gate
(lib/records/canon-parity-reach.test.ts) and the era test
(lib/records/canonical-era.test.ts) continue to run unchanged.