Format compatibility
The Payload discriminants and the canonical encoding are frozen.
Changing them invalidates every already-sealed log. A new payload type takes
the next free integer; nothing is ever renumbered.
The rule has already been exercised several times. Actor (tag 7) was added
after the fact for the actor chain, in preference to adding a field to
Delegation; ConfigReload (tag 8), SessionCert (tag 9) and McpAccess
(tag 10) followed the same way. The record_format_is_frozen test carries
reference hashes for the existing payloads; none of them moved. The day that
test fails, the question is not “how do I update the constants” but “which
sealed logs have just been invalidated”.
Signed bundles evolve differently
Section titled “Signed bundles evolve differently”A bundle’s format string is part of the signed bytes, so a revision is a new string, and every revision an artifact was published under keeps verifying with the signing bytes of its day.
Exercised twice:
obsign-identity/2extended the signed bytes with the machine markers;obsign-policy/2extended them with the argument declarations.
Both follow the same two rules:
- a
/1bundle keeps its hash and signature; - a
/1file carrying fields only/2signs is refused rather than trusted, since otherwise those fields would be unsigned authority.
The control plane emits /2 only when a deployment actually uses the new
feature, so a fleet that never does never forces a gateway upgrade.
Why the encoding is canonical
Section titled “Why the encoding is canonical”JSON allows too much freedom (key order, whitespace, numbers): two
serializers can produce two hashes for the same data. In the canonical
encoding every field is length-prefixed and concatenation is injective. JSON
stays the transport and reading format, and never the computation one.
obsign-audit-core is the only place a hash is computed, shared by the
gateway, the ledger, the control plane and the verifier alike.