Expand description
v0.177 (#643): the canonical normal form of a cross-context contract, and its hash.
A workers build compiles context A against context B’s contract, and
nothing at runtime checks that the deployed B still matches what A was
compiled against — deploy --context NAME institutionalises the skew. The
fix is to stamp a hash of the compiled contract beside X-Bynk-Caller and
fail closed on mismatch (ADR 0092’s pattern: a compile-time constant in a
reserved header, metadata beside the payload, no crypto).
The hash is only as good as the form it hashes. Two rules make it usable:
- Semantically-equal contracts must hash equal, or a working deployment 409s spuriously — which is worse than no check at all, because it breaks what worked and destroys trust in the mechanism. This is why the form is canonical (predicates as a sorted set, record fields sorted by name) rather than a rendering of source order.
- Both sides must canonicalise the same thing. The callee’s contract
is canonicalised in the callee’s own namespace, from the callee’s own
type table, on both sides — never in the caller’s. The caller reaches
that table through
consumed_types[callee]and the callee through its own combined table; both are produced by the samecombined_types_for, so the two views cannot diverge by construction. A caller never canonicalises a consumed type in its own namespace, where its rebranding would make the same type render differently.
Constants§
- FNV1
A64_ OFFSET - FNV-1a 64-bit offset basis. Published in the wire-format reference (#1969).
- FNV1
A64_ PRIME - FNV-1a 64-bit prime. Published in the wire-format reference (#1969).
Functions§
- canon_
capability_ op_ signature - P8.1 (#1512, [DECISION C]): the canonical form of a
CapabilityOpSignature— added by PR #1517’s own bot review (finding #3): acapabilitydeclaration’s own ops are the abstract signature a consumer’sCap.op(...)call site compiles against. - canon_
fn_ signature - P8.1 (#1512, [DECISION C]): the canonical form of a
FnSignature— extends this module’s owncanon_type(ADR 0200) to a shape it doesn’t reach today. Body-free by construction ([DECISION B]): there is nobody/requires/ensuresfield onFnSignatureto accidentally include here. - canon_
handler_ signature - P8.1 (#1512, [DECISION C]): the canonical form of a
HandlerSignature.givenrenders sorted — its renderedCapRefnames have no meaningful order to preserve (it’s a set, not a sequence).kindis rendered first (PR #1517’s own bot review, finding #1) — two handlers that differ only in HTTP method or route, or inon callvs.on message, must not canonicalise identically. - canon_
predicate - canon_
refinement - Predicates canonicalise as a sorted set.
- canon_
storage_ type - State-migrations S0 (#2204, track §3.1): the two canonical renderings of a
stored type that
bynk.schema.lock’s[agents]table records — the structure (.0, predicates stripped) and the full form (.1, predicates kept, opaque ones included). Both expand named records and sums structurally and sort fields and variants by name, as the contract form does. Unlike it, they carry no type names, because stored bytes carry none: recursion closes with a positional@Nback-reference instead of@Name, and a sum payload renders like a record (name: type, sorted), because a stored payload is keyed by field name. - canon_
store_ field_ signature - P8.1 (#1512, [DECISION C]): the canonical form of a
StoreFieldSignature. - canon_
unit_ signature - P8.1 (#1512, [DECISION C]):
UnitSignature::canonical’s own implementation — the single rendering R3.14’s own stability proof compares. Every category is rendered sorted (by the caller’s ownBTreeMap/BTreeSetkeys, or an explicit sort forVec-shaped fields like a handler’s owngiven), so construction order never perturbs the form — the same “compare the canonical string, not map/vec iteration order” disciplineservice_normal_form’s record-field sort already established for ADR 0200. - contract_
hash - FNV-1a (64-bit) over the canonical form, rendered as 16 lowercase hex chars.
- fnv1a64_
hex - FNV-1a (64-bit) over
bytes, as 16 lowercase hex chars —contract_hash’s algorithm over raw bytes. #1883, slice 1b also uses it for an asset’s content hash (assets::served_path), where the same reasoning holds: it only has to tell one project’s own files apart, reproducibly. - own_
contract_ hashes - v0.177 (#643): a context’s own
on callcontract hashes, keyed by service name — the constants its Worker entry compares an incomingX-Bynk-Contractagainst. - service_
contract_ hash - The stamped contract hash for one consumed service.
- service_
normal_ form - The canonical normal form of one
on callservice contract.