# Canonicalization — recipe v1.1 (the I16-corrected edition)

Everything WoC signs is hashed over **one canonical form**, implemented by ONE
module, byte-identical across repos:

- `woc/lib/records/canonical-form.mjs`
- `acn-mcp-server/kernel/records_canonical_form.mjs`

Cross-repo pin (both files, sha256): `ba62f43fcabadf926d72d4e5c96c21ace2feffb8040967e30dd2808245697316`

## The recipe

`stableStringify(value)`:

1. Arrays: stringify each element in order.
2. Objects: sort own keys lexicographically, build a **NULL-PROTOTYPE**
   accumulator (`Object.create(null)`), recurse per value.
3. Serialize with `JSON.stringify` (no whitespace).

The null-prototype accumulator is load-bearing (v1.1, the reviewer's I10):
writing into `{}` invokes the `__proto__` prototype setter, so an own
`__proto__` field would silently VANISH from the canonical form. With the
null-proto accumulator an own `__proto__` key survives as a normal own key —
it AFFECTS the signed bytes, so a smuggled field breaks the signature, and the
strict record schema rejects such input outright.

## The signed scope of a record

`canonicalFormOf(record)` = `stableStringify(record` minus `signature`,
`signing`, `derivation``)`. Everything else — including `public_key`,
`issued_at`, and `dependency_manifest_sha256` — is inside the signed bytes.

## The dependency-manifest digest (v1.1, the reviewer's I16)

`dependency_manifest_sha256` = `sha256(stableStringify(dependency_manifest))`
— the CANONICAL recipe, never insertion order. Equal manifest values hash
equally regardless of stored key order; the acceptance test is the round trip
JSON → canonical JSON → parsed object → same digest.

Historical note: the r3 record `mo-dental-013494@2` carries a superseded
stored-order digest (packaged `bf102ea8…`, canonical `514e3718…`). Per the
reviewer's rank-4 law, signed history remains inspectable — the verifier
labels it with a non-blocking note in record mode and REJECTS it in
`--current` mode (current reliance requires internal consistency under the
current recipe). The successor `@3` carries the canonical-recipe digest
(`514e3718…`).

## Signing

Ed25519 over SHA-256 of the canonical form. Production signer: int1
(`woc-int1-2026-09`, root-held private key; public key served at
`/.well-known/woc-issuer-keys.json` and pinned in this package's
`issuer-trust.json`). The fixture key signs only the labeled conformance
fixture and never authorizes a production record.