# E37 protocol spec (normative for the clean-room verifier)

Encoding: canonical JSON (sorted keys, `(",",":")` separators, UTF-8, `ensure_ascii=False`).
`H(d, o) = SHA-256(canonical({"d": d, "o": o}))`, hex. Signatures: Ed25519 over `canonical({"d": schema, "o": body-without-sig})`.

## AFO `cain.govfederation.afo.v1`
- `federation_state_root = H("cain.govfederation.state.v1", body minus signatures and the root itself)`
- `afo_hash = H(afo schema, afo minus signatures)`
- `signatures[party]` is required for both `domain_a` and `domain_b`, and is verified against the verifier's pins.
- Errors, in this order: NOT_AN_AFO, AFO_GRANTS_AUTHORITY, AFO_STATE_ROOT_MISMATCH, then per party in a,b
  order: UNKNOWN_DOMAIN:x, MISSING_SIGNATURE:x, BAD_AFO_SIGNATURE:x.

## AGN messages `cain.govfederation.agn.message.v1`
Fields: federation_id, sender, receiver, kind, seq (= transcript length), prev_hash (= H(msg schema, previous
message) or 64 zeros), epoch, nonce (= H("cain.govfederation.agn.nonce.v1", [fid, sender, seq, epoch])),
payload, sig. Rules: sender pinned; IDENTIFY once per party before REQUIREMENTS; requirements only from their
own domain and immutable; OFFER only after the other side's requirements; a COUNTEROFFER may not keep a
rejected condition; ACCEPT only for the latest counterparty offer that satisfies the accepter's requirement.

## Gate: ordered checks (first failure is the reason)
1 NOT_A_REQUEST · 2 UNKNOWN_FEDERATION · 3 WRONG_TARGET_DOMAIN · 4 REQUESTER_NOT_PARTY · 5 BAD_REQUEST_SIGNATURE
· 6 FEDERATION_<status> unless ACTIVE · 7 IDENTITY_MISMATCH · 8 EPOCH_MISMATCH · 9 FEDERATION_EXPIRED (contract or
AFO window) · 10 STALE_REQUEST (issued in future or older than freshness_requirement) · 11 NONCE_REPLAY ·
12 assurance: NOT_AN_ASSURANCE, ASSURANCE_ISSUER_NOT_ACCEPTED, ASSURANCE_GRANTS_AUTHORITY, BAD_ASSURANCE_SIGNATURE,
ASSURANCE_SUBJECT_MISMATCH, ASSURANCE_REVOKED, ASSURANCE_EXPIRED, ASSURANCE_STALE, ASSURANCE_INSUFFICIENT ·
13 INTEGRITY_DRIFT:<key> (attested digests, sorted keys) · 14 DISPUTE_IS_NOT_APPROVAL (any dispute not
RESOLVED/CLOSED) · 15 DELEGATION_NOT_IN_CONTRACT · 16 COLLECTIVE_AUTHORITY_AMPLIFICATION (cap not in
intersection of members ∩ contract) · 17 CAPABILITY_NOT_IN_CONTRACT (contract and AFO accepted) ·
18 CAPABILITY_NOT_IN_LOCAL_AUTHORITY · 19 CONTRACT_EXCEEDS_LOCAL_AUTHORITY · 20 SOVEREIGNTY_BOUNDARY (boundary
dims + contract data/execution scope) · 21 LOCAL_POLICY_<value> (missing = UNKNOWN) · 22
FEDERATION_BUDGET_EXCEEDED (amount <= 0, or usage + amount > min(contract resource_scope, AFO resource limit)).

ALLOW issues the local authorization {schema, issuer, federation_id, request_hash = H(request schema, signed
request), capability, state_root, epoch, issued_ms = now, expires_ms = now + 50,
grants_authority_beyond_request: false}, with `auth_id = H(local_authorization schema, that object)`.

`state_root = H("cain.govfederation.local_state.v1", {federation_id, afo: afo_hash, contract: contract_hash,
assurance: assurance_id, status, version, disputes: sorted [[id, state]], authority: H("a", authority),
policy: H("p", policy), boundary: H("b", boundary), revoked: sorted ids})`.

## MCPGate
NO_LOCAL_AUTHORIZATION, BAD_LOCAL_AUTHORIZATION (issuer/sig/auth_id), AUTHORIZATION_CONSUMED,
AUTHORIZATION_REQUEST_MISMATCH, AUTHORIZATION_EXPIRED, FEDERATION_NOT_ACTIVE, STALE_AUTHORIZATION_STATE_ROOT.

## Fork
Each history entry is {seq, contract_hash, afo_hash}. A fork is two distinct (contract_hash, afo_hash)
pairs at the same seq across any observers. A shorter prefix alone is not a fork.
