Draft for community review. Feedback welcome.
This is a working draft published for comment. It is not a ratified standard, and normative requirements may change before v1.0. · Read the raw markdown →
An extension to the Agent2Agent (A2A) protocol for verifiable bilateral commitments between organizations.
| Extension URI | https://anaxstandard.ai/ext/governed-crossing/v1 |
| Version | v0.4.4 DRAFT |
| Date | 2026-09-17 |
| Licence | CC BY 4.0 (CC-BY-4.0) for this text and its conformance vectors — attribution "ANAX Standard — anaxstandard.ai"; no licence is required to implement. Reference implementations: Apache-2.0. See §9A. |
| Trademarks | Governed Crossing™, Crossing Seal™, Decision Passport™ — ANAX Standard. Operating a Registration Authority, Verification Authority or Centre is a service, not licensed by this document (§9A). |
| Publisher | ANAX Standard — ANAX Institute |
| Contact | contact@viccaone.com |
| Verification Authority | verify.anaxstandard.ai |
Status: Draft for community review. Feedback welcome.
This is a working draft published for comment. It is not a ratified standard. Normative requirements may change before v1.0. Implementers are welcome, and are asked to write to the contact address above — in particular where a requirement is unimplementable, ambiguous, or wrong.
⚠ Nothing in this specification is implemented or enforced.
v0.3 fixes a vocabulary and a wire contract. No component enforces either. The conformance vectors for the commitment-class rules and for the two methods both fail, completely, as of this publication — see §10 item 6. This is stated here, at the top, because a specification that reads as though it describes a running system is worse than one that says plainly that it does not.
Note on this document. Normative content is specification; implementation notes reflect the reference implementation.
| Reference | Used for |
|---|---|
A2A — Agent2Agent protocol specification (Linux Foundation), a2a-protocol.org |
Core object model, TaskState, RPC method conventions, per-transport method naming |
| A2A extension mechanism — extensions documentation | AgentExtension, activation header, extension types, constraints |
AP2 — Agent Payments Protocol specification, ap2-protocol.org |
Mandate model, open/closed variants, SD-JWT basis, signing requirements |
| RFC 2119 / RFC 8174 | Requirement keywords |
| RFC 8785 — JSON Canonicalization Scheme (JCS) | Normative canonicalization for the Terms Document (§4.2, §5.8) |
| SD-JWT — Selective Disclosure JWT | Agreement Mandate encoding |
Informative. "Governance Gaps in Agent Interoperability Protocols: What MCP, A2A, and ACP Cannot Express", Kang & Diponegoro, arXiv 2606.31498, 30 June 2026 — the analysis this specification responds to.
Normative statements that describe capability not yet present in the reference implementation are marked IMPLEMENTATION PENDING. The specification defines the target; implementations catch up. Such a marker is not an admission of weakness — it is the point of writing a specification before code.
⚠ As of v0.3 that marker applies to the whole of §3.7 and to the class and magnitude rules of §4.1.3. See §10, item 11-16.
AP2 proves the user paid. Governed Crossing proves the deal should have been made.
A2A standardizes how agents talk. AP2 standardizes how agents pay. Neither expresses whether a commitment was authorized, defensible, and auditable.
The SWIFT analogy. In 1973 banks already moved money by telex. SWIFT did not invent the message — it standardized trust in the message, and became mandatory infrastructure for fifty years. In 2026 agents already talk (A2A) and already pay (AP2); nobody has standardized trust in the agreement.
A SWIFT message is inherently bilateral — a sender institution and a receiver institution — and the whole value of the standard is that both ends can trust an instruction crossing between two organizations. Governed Crossing exists to give agent-to-agent commerce that shape.
This specification defines Governed Crossing, an extension to the Agent2Agent (A2A) protocol that allows two organizations, each represented by an agent, to make a verifiable joint commitment — and to prove afterwards that each side held authority to make it.
A2A standardizes how agents talk. AP2 standardizes how agents pay. Neither expresses whether a commitment was authorized, defensible, and auditable. That gap is independently documented.
Governed Crossing is that layer, expressed as an A2A extension so that it composes with the ecosystem rather than competing with it.
pack_id, pack_version) and its outcome, not its reasoning.Two classes. An implementation MUST declare which it satisfies.
An implementation is Governed Crossing Verify-Only capable if it:
Artifact.metadata (§4.3) without failing on unknown keys.required: false.Verify-Only exists so that an agent can be a credible counterparty in one direction — able to check what it is being shown — without operating a sealing infrastructure. It is the on-ramp, and it is deliberately cheap.
An implementation is Governed Crossing Full capable if it satisfies Class 1 and additionally:
required: true for any skill
that can produce a commitment.⚖ DESIGN LAW (inherited): the gate must be the only path out. A Full implementation MUST NOT expose a commitment-emitting capability that is reachable without passing the sealing path.
Inherited design law. Normative for ANAX's own implementation; informative for third parties.
| Tier | Produces a Mandate? | Produces a Crossing Seal? | Tempo |
|---|---|---|---|
| T0 — explanation, context, guidance | No | No | Instant. Unsealed by design; nothing here is a commitment. |
| T1 — commitments within standing authority | Yes — Closed Mandate signed by the agent | Yes. MUST. | Synchronous hold-until-sealed at business tempo (~25s), surfaced as "verification in progress". |
| T2 — novel legal obligation | Yes — Closed Mandate signed by an authorized human | Yes. MUST. | Human-gated. |
⚖ DESIGN LAW (inherited): the tier decides the tempo. T0 never waits; T1 never sends before its seal exists. There is no third mode, and in particular no fast path for a T1 commitment judged low-risk — that judgement is itself a T1 output.
The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in RFC 2119 and RFC 8174, and appear in bold uppercase when used normatively. Where these words appear in lowercase they carry their ordinary English meaning and impose no requirement.
Governed Crossing™ : The A2A extension defined by this specification, and by metonymy a single instance of its use: one bilateral commitment made under verified mandates and sealed. This is the name used on the wire.
Crossing Seal™ : The sealed, verifiable record of a Governed Crossing. A cryptographic commitment over the bilateral record (§4.3), written into an append-only chain and resolvable at the Verification Authority. This is the name used on the wire for the sealed object.
Agreement Mandate : A signed, machine-checkable statement of authority to commit, in Open and Closed variants (§4.1). The wire form of the Mandate primitive: who holds it, what commitment class it covers, what magnitude limit applies, when it expires.
Principal : The organization on whose behalf an agent acts and which bears the commitment. A Principal is an organization, not a natural person. The Principal issues Open Agreement Mandates. * differs from AP2, whose mandates originate with a natural person via a Trusted Surface.)*
Counterparty : The other Principal in a Governed Crossing, and by extension its agent. The presence of a Counterparty is what distinguishes a Governed Crossing from today's unilateral Canal Crossing.
Verification Authority
: The service that resolves a Crossing Seal and answers whether it is genuine and what it commits to.
For this specification the Verification Authority is verify.anaxstandard.ai.
The certificate registry is a separate surface and is NOT a Verification Authority for
Governed Crossing.
Commitment Class : The category of obligation a commitment creates. Determines which Mandate applies and whether human signature is required. IMPLEMENTATION PENDING — the vocabulary is defined by the committing Principal's governance pack; ANAX's own vocabulary is the T0/T1/T2 tier model (§1.5).
Magnitude Limit : The ceiling — monetary, temporal, or scope-based — above which a Closed Mandate may not be signed under standing authority. IMPLEMENTATION PENDING.
Terms Document
: The canonical serialization of what was agreed. Its hash is agreement_hash (§4.1.3). *
ANAX MUST define this canonicalization; AP2 inherits one for free from a merchant-issued artifact and
ANAX has no equivalent.)*
Bilateral Record : The complete object sealed by a Crossing Seal: two Agreement Mandates, a terms hash, participant identities, and timestamps (§4.4).
Inherited design law. The rationale below is reproduced verbatim, as required, because it is the reason the naming is locked and will be quoted when the naming is questioned.
On the wire: "Governed Crossing" and "Crossing Seal". Never "Passport" at protocol level.
What A2A's Secure Passport actually is. Measured: a contextual state-sharing extension. A Calling Agent voluntarily shares a structured subset of its context — user preferences, previously verified identity tokens, session history — with a Called Agent, without exposing its whole internal memory. It is a convenience and personalisation primitive, positioned as a middle ground between A2A's stateless design and the practical need for shared context.
Assessment: collision, not opportunity — at the level of the name. The two objects are close enough to be confused and far enough apart to be misleading in exactly the direction ANAX exists to prevent:
| A2A Secure Passport | ANAX Decision Passport | |
|---|---|---|
| who asserts it | the caller, about itself | a third party, about the caller |
| what it is for | personalisation | attestation |
| verifiable by | the called agent | anyone, at a public URL |
A prospect who hears "ANAX Passport" in an A2A context will read it as "the A2A one, extended" — i.e. as a self-asserted claim. And A2A's is the one with the Linux Foundation and 150+ organisations behind it. ANAX will not win that naming contest and should not enter it.
The real opportunity, stated honestly. The two compose. An A2A Secure Passport can carry, as one of its shared context items, a reference to a Governed Crossing seal. That is a strong story — "their passport says what I claim about myself; ours says what was verified about me" — and it is available precisely because the names differ. Renaming is not a concession; it is what makes the composition legible.
Normative consequence. An implementation MUST NOT use the term "Passport" for any object defined by
this specification, in any protocol field, URI, method name, log line, or user-facing string emitted during
a Governed Crossing. "Decision Passport" remains the correct name for the ANAX-side artifact and product at
verify.anaxstandard.ai, and MAY be used there.
A2A identifies an extension by a URI. Extensions hosted by the A2A project itself use
https://a2a-protocol.org/extensions/{name}/v1; third parties publish under their own authority.
Extension URI — APPROVED (2026-08-09):
https://anaxstandard.ai/ext/governed-crossing/v1Frozen on publication.
The URI is the permanent identifier of the extension and is subject to the same discipline as an issued identifier namespace:
⚖ DESIGN LAW (inherited):
a namespace is FROZEN once published. Changing this URI after any counterparty has activated it breaks
every prior activation and every stored reference. A version increment (/v2) is the only permitted
change, and it MUST be additive — /v1 MUST remain resolvable.
anaxstandard.ai is used rather than .com for consistency with the Verification Authority (§2.2).
Backlog (§11-1): the URI MUST eventually serve the specification document itself. An extension URI that dereferences to nothing is a broken promise to every implementer who follows it, and A2A's whole discovery model assumes a reader can resolve an unfamiliar extension. Not required for this draft; required before publication.
Per A2A's four extension types:
| Type | Used by Governed Crossing | For |
|---|---|---|
| Data-only | Yes | Declaring the Principal's clearance envelope and trust basis on the Agent Card (§3.4). |
| Profile | Yes | Overlaying pre-commitment state requirements on core messages — mandates MUST be exchanged and verified before a commitment message is honoured. |
| Method (Extended Skills) | Yes | crossing:seal and crossing:verify (§3.5). |
| State Machine | Yes, with a constraint | An extension-scoped commitment state. This is NOT a new TaskState value —.5. |
Governed Crossing is therefore a composite extension. This is permitted; the four types are descriptive categories, not mutually exclusive declarations [D — the extensions documentation presents them as things an extension "can add", not as an enumeration to choose from].
An agent declares support by including an AgentExtension object in AgentCapabilities.extensions.
AgentExtension fields: uri, description,
required, params.
*(*CONFIRMED 2026-09-01. The rendered object list in specification.md showed only uri,
description, required, while extensions.md documented params with a worked example. Checked against
the normative definition: the A2A protocol buffers definition declares google.protobuf.Struct params = 4 on
AgentExtension, and A2A v0.3.0 §5.5.2.1 gives params?: { [key: string]: any }. params is normative;
this declaration stands as written and no move to a Data-only surface is needed.)
{
"uri": "https://anaxstandard.ai/ext/governed-crossing/v1",
"description": "Bilateral commitments under verified mandates, sealed and publicly verifiable.",
"required": true,
"params": {
"conformance": "full", // "verify-only" | "full"
"verification_authority": "https://verify.anaxstandard.ai/",
"principal": {
"id": "urn:anax:principal:<opaque>", // stable identifier of the Principal
"clearance_id": "<envelope id>", // IMPLEMENTATION PENDING — see §3.4
"trust_basis": "vicca_authority_sealed", // see §3.4
"agc_level": 3
},
"mandate_formats": ["sd-jwt"],
"commitment_classes": ["<class>", "..."] // classes this agent can commit within
}
}
Normative requirements.
params.conformance MUST be present and MUST be one of "verify-only" or "full".required: true for skills that can produce a commitment, and
MUST NOT declare required: true on a skill that cannot.trust_basis it cannot substantiate at the Verification Authority.params.principal carries the Principal's standing governance posture. Its fields map onto the existing
ANAX System Clearance Envelope.
The envelope is already an onboarding gate in the existing implementation: without a clearance envelope, the onboarding gate returns a VICCA-required outcome and issues no passport. That gate is inherited: an agent without a clearance envelope MUST NOT be Full-conformant.
trust_basis values are the existing evidence trust hierarchy: self_declared, organisational_controls,
third_party_audited, vicca_authority_sealed, nca_verified.
Clarification of a term the design analysis corrected. This is an evidence trust hierarchy — how good the evidence behind the Principal's posture is. It is not a trust relation between two parties, and MUST NOT be interpreted as one. A Counterparty's
trust_basissays how well-evidenced its own governance posture is; it says nothing about whether you should trust it .
Activation is per-request, via the A2A-Extensions HTTP header carrying a comma-separated list of
extension URIs. The responding agent echoes back, in the same header, the extensions it actually activated.
Extensions default to inactive.
POST /message:send HTTP/1.1
A2A-Extensions: https://anaxstandard.ai/ext/governed-crossing/v1
⚠ This is the most consequential review finding, and it refines a security assumption in the design analysis.
A2A's required: true means an agent SHOULD reject requests from clients that have not activated a
required extension. SHOULD, not MUST — and the rejection is the agent's choice, evaluated
per-request. It follows that "simply do not send the header" is an available downgrade path by design of
A2A. The anti-downgrade property cannot be inherited from the transport; this specification must
impose it.
Because the transport cannot enforce this, the endpoints must. These two requirements are the load-bearing pair of the entire specification, and every other normative statement about downgrade derives from them:
OUTBOUND. A Class 2 implementation MUST NOT issue a commitment without a sealed Governed Crossing.
INBOUND. A Class 2 implementation MUST reject any inbound commitment that does not carry a valid Crossing Seal.
Defense lives at the endpoints, not the transport. The consequence is the point: an attempted downgrade stops being a silent path and becomes a detectable violation. A party that omits the header, or presents a commitment without a seal, is not quietly served a lesser service — it is refused, and the refusal is attributable. A protocol cannot prevent a peer from behaving badly; it can guarantee that behaving badly fails visibly.
Full treatment, including the disclosure of the transport property that makes this necessary: §7.4.
Two new RPC methods. A2A's REST naming convention is a resource with a colon-verb —
POST /message:send, POST /tasks/{id}:cancel; the JSON-RPC and
gRPC forms differ — see the binding table below.
| Method | Shape | Purpose |
|---|---|---|
crossing:seal |
see binding table below | Submit a Bilateral Record for joint sealing; returns a Crossing Seal. |
crossing:verify |
see binding table below | Resolve a Crossing Seal. Delegates to the Verification Authority. |
Per-transport binding. A2A v0.3.0 fixes extension method naming per transport in three subsections:
§3.5.5 (Extension Method Naming) requires JSON-RPC extension methods to follow {category}/{action}
with a clear namespace (its own example: myorg.extension/action); §3.5.3 requires REST endpoints to
use /v1/{resource}[/{id}][:{action}] with colon notation for non-CRUD actions; §3.5.2 requires gRPC
methods to use PascalCase. §3.4.1 requires functional equivalence across transports.
| Operation | JSON-RPC method |
REST | gRPC |
|---|---|---|---|
| seal | anaxstandard.crossing/seal |
POST /v1/crossing:seal |
SealCrossing |
| verify | anaxstandard.crossing/verify |
POST /v1/crossing:verify |
VerifyCrossing |
crossing:seal / crossing:verify are the canonical operation names used throughout this
specification; the table above is their transport binding. (an earlier draft used crossing/seal, which was
correct for JSON-RPC and lacked only the namespace — §10.)A2A-Extensions header (§3.5) MUST name the extension URI on every request to either
method. A request without it is refused with EXT_NOT_ACTIVATED — HTTP 428 Precondition Required, a
missing precondition rather than a denied right (§3.7.4). This is the endpoint-side half of
"no seal, no deal" (§3.5.1).message/send: the core methods carry the negotiation (mandate exchange, Terms
Document) and receive the sealed Artifact. crossing:seal is the commitment step, invoked once per
crossing after both Closed Mandates are verified (§5.4), by the initiator against the responder's
endpoint; the responder returns the Crossing Seal and the initiator attaches it to the agreement Artifact
(§4.3.1) in the terminal message/send / message/stream exchange. crossing:verify is exposed by the
Verification Authority and MAY be proxied by any agent; its result is a projection, never an
authority.The wire contract for both methods — params, result, errors, determinism — is §3.7.
Merged from Addendum A-3, which closed §11-6. §3.6 names the methods and binds them to transports; this section is what a caller and a responder must agree on to interoperate.
crossing:seal — paramsAll fields REQUIRED unless marked.
| Field | Type | Meaning |
|---|---|---|
record |
object | The Bilateral Record (§4.4) with chain position excluded (prev_hash, chain_seq omitted) — the party-independent input to joint_seal. |
initiator_closed_mandate |
string (SD-JWT) | The initiator's Closed Mandate, by value — the responder verifies it (§5.4). |
initiator_anchor_intent |
object {prev_hash, chain_seq} |
The initiator's own chain position, so the responder can check it is well-formed; NOT sealed into joint_seal. |
idempotency_key |
string | MUST equal record.crossing_id. Present explicitly so transports without a body-level convention still carry it. |
Responder behaviour (normative). Verify the initiator's Closed Mandate and its own (§5.4, all checks);
recompute agreement_hash from the Terms Document it holds (§4.2) and require equality with
record.agreement_hash; check the class and magnitude rules L-1, M-2, M-3 and M-4 (§4.1.3) against both
mandates; compute joint_seal (§4.4 req 4); write its own anchor into its chain; return the result.
Idempotent on crossing_id (§5.6 req 3): a repeat returns the existing result and MUST NOT write a
second anchor.
crossing:seal — result{
"crossing_id": "<uuid>",
"joint_seal": "<sha256 hex>",
"seal_id": "canal-<ts>-<rand>", // the responder's public identifier for the seal
"sealed_at": "<RFC3339>",
"responder_anchor": { "prev_hash": "<sha256|null>", "chain_seq": 42 },
"verify_url": "https://verify.anaxstandard.ai/?id=canal-<ts>-<rand>",
"artifact_block": { /* the exact §4.3.2 block, ready for Artifact.metadata[uri] */ }
}
Effector consumption — normative (v0.4 note, 2026-09-02). An effector is a party that performs
crossing:seal on behalf of an agent whose Mandate it did not issue — a gateway, a broker, a second
implementer's seal endpoint. When an effector consumes a unit of an agent's magnitude_limit at the
issuing Registration Authority, the following are REQUIRED:
token_sha256,
unique at issuance) and MUST NOT accept an identifier in its place. Knowledge of a Mandate's id is not
possession of the Mandate; a tampered token MUST be indistinguishable from one never issued.SERVICE_HELD_MANDATE, any consumption by an effector of a Mandate whose subject is one that any
registered issuer is permitted to issue for. Mandates held by services in their own right — a
monitoring service's read grant, a provisioning service's issuance grant — are never spendable by an
effector, even when the effector holds the token. This is the lock for the day a service-held
token leaks: without it, a compromised or careless effector can drain a service's authority from
outside.ROLE_MISMATCH. A party configured
with both capabilities is a party that governs other agents' authority and quietly holds its own.⚠ A second implementer building an effector MUST honour rule 2 or it builds an effector that can
drain service Mandates. The rule is stated here, and not only in the reference implementation's
code, for exactly that reason. Conformance: the acceptance test presents a service-held token from the
effector and requires SERVICE_HELD_MANDATE with nothing consumed (reference-implementation acceptance test).
crossing:verify — params and resultv0.4 (adopted 2026-09-06) — where the two methods live, and under which URI.
crossing:sealandcrossing:verifyare methods of this extension —https://anaxstandard.ai/ext/governed-crossing/v1— and no separate URI exists or is declared for them; theA2A-Extensionsheader names that one URI for both.crossing:sealis an act and is served by the responder (Centre-001).crossing:verifyis a resolution and is served by the Verification Authority (§3.1,verify.anaxstandard.ai) as one more resolver branch of the single public verify surface — the single-portal rule: one portal resolves every ANAX identifier, structurally, without login. A responder MAY additionally answercrossing:verifyfor its own seals; the Verification Authority's answer is the public one and the conformance target (WC-5, WC-6).
params: exactly one of seal_id (string) or crossing_id (uuid). Optional record (object): if
supplied, the verifier additionally checks §6.2 item 2 (recomputed seal equals stored seal).
result: the public projection — the §4.3.2 block fields crossing_state, seal_id, seal,
prev_hash, chain_seq, is_genesis, seal_version, pack_id, pack_version, outcome, sealed_at,
verify_url, plus verified_by (the VA identifier, e.g. VA-001) and checks: an object with one boolean
per §6.2 check (resolves, seal_matches — null if no record supplied — chain_consistent,
basis_present, not_superseded). clearance_id MUST NOT appear (§4.3.2). A non-resolving identifier
returns a result with resolves:false and every other check null — a verdict, not an error (§6.2:
a seal that does not resolve is a commitment that was not made).
crossing:verify is public: no activation header, no authentication. It is rate-limited and cacheable;
a sealed record is immutable, so a resolves:true result MAY be cached for the seal's lifetime.
Errors are JSON-RPC error objects (A2A §6.12) with code in the range −40000 to −40099 — outside the
JSON-RPC reserved range −32768…−32000 that A2A's own codes occupy — and data.error carrying the name
below. Implementers switch on the name; the number exists for JSON-RPC compliance. REST maps to the HTTP
status shown; gRPC to the equivalent status per A2A §3.2.2.
The Spec failure path column is the point of this table. A name without the rule that produces it is a
string an implementer has to guess at; every error here is traceable to the requirement it enforces.
| Name | Code | HTTP | Spec failure path | Raised by |
|---|---|---|---|---|
EXT_NOT_ACTIVATED |
−40001 | 428 | §3.5.2 req 1 | seal |
MANDATE_INVALID |
−40010 | 401 | §5.4 (signature / structure) | seal |
MANDATE_EXPIRED |
−40011 | 401 | F1 | seal |
MANDATE_AUDIENCE |
−40012 | 403 | §5.4 aud |
seal |
MANDATE_NONCE_REPLAY |
−40013 | 409 | §7.1 | seal |
MANDATE_REVOKED |
−40014 | 401 | §5.4 revocation at commit (the Mandate Object Specification, forthcoming, §2.4) | seal |
AGREEMENT_HASH_MISMATCH |
−40020 | 422 | §4.2 | seal |
CLASS_EXCEEDED |
−40030 | 403 | §4.1.3 L-1 (CM-2) | seal |
MAGNITUDE_EXHAUSTED |
−40031 | 403 | §4.1.3 M-4 (CM-1) | seal |
NARROWING_VIOLATION |
−40032 | 403 | §4.1.3 L-2 / M-3 (CM-3) | seal |
CURRENCY_MISMATCH |
−40033 | 422 | §4.1.3 M-2 (CM-4) | seal |
MALFORMED_MANDATE |
−40034 | 400 | §4.1.3 L-5 / M-2 (CM-5, CM-6) | seal |
MAGNITUDE_MISMATCH |
−40035 | 403 | §5.4 magnitude equality — the record's magnitude_asserted must equal the Closed Mandate's asserted magnitude; two sealed documents disagreeing on the committed amount are refused, not sealed |
seal |
COUNTERPARTY_NOT_PERMITTED |
−40040 | 403 | §4.1.3 permitted_counterparties |
seal |
ROLE_MISMATCH |
−40041 | 403 | §3.7.2 effector rule 3 | seal (RA) |
SERVICE_HELD_MANDATE |
−40042 | 403 | §3.7.2 effector rule 2 (acceptance test) | seal (RA) |
MANDATE_PARTY_MISMATCH |
−40043 | 403 | §4.4 req 7 / §5.4 check 9 (v0.4) | seal |
CHAIN_POSITION_UNAVAILABLE |
−40050 | 503 | §4.4 req 5 / F6 | seal |
SEAL_MISMATCH |
−40051 | 409 | §4.4 req 6 | initiator-side (reported, not returned) |
RECORD_MALFORMED |
−40060 | 400 | §4.4 reqs 1–3 | seal, verify |
VERIFY_PARAMS |
−40070 | 400 | §3.7.3 (neither/both identifiers) | verify |
RATE_LIMITED |
−40090 | 429 | §3.7.3 | verify |
crossing_id and the
timestamp (§3.5.2 req 4 — a refusal not recorded is a detection thrown away).crossing:verify never returns an error for an unknown identifier (§3.7.3); errors are for
malformed calls and rate limiting only./v1.Both methods are deterministic for a given input and store state: two verifiers with the same seal store
return the same crossing:verify result; two responders with the same mandates and record compute the same
joint_seal (§4.2 fixes the bytes). This is truth-gate Π2 stated at the wire.
Method-level evolution is governed by the extension URI (§3.1): additive changes — new optional params, new
result fields, new error names — stay within /v1; any incompatible change is /v2 with /v1 remaining
resolvable. Implementations MUST ignore unknown result fields.
EXT_NOT_ACTIVATED, nothing written, refusal recorded.joint_seal equal to the initiator's own computation.crossing_id → byte-identical result, anchor count unchanged.record.agreement_hash ≠ recomputed → AGREEMENT_HASH_MISMATCH.seal_id of WC-2 → resolves:true, all checks true, no clearance_id key in the result.seal_id → resolves:false, other checks null, no error.agent_id is a different subject →
MANDATE_PARTY_MISMATCH, nothing written, refusal recorded.record.parties[initiator].closed_mandate_hash ≠ SHA-256(initiator_closed_mandate) →
MANDATE_PARTY_MISMATCH, nothing written, refusal recorded.chain_ids,
both chain_seq: 0, both prev_hash: null (two geneses); a third seal with the first counterparty → that
chain's chain_seq: 1 with prev_hash = its own genesis anchor and no reference to the other chain.MANDATE_REVOKED; nothing sealed, no anchor, the refusal recorded with
initiator_spent: true, responder_spent: true.magnitude_asserted differs from a party's Closed Mandate's asserted
magnitude, both within the Mandate's limit → MAGNITUDE_MISMATCH, nothing written, nothing consumed,
refusal recorded.⚠ WC-1…WC-7 all fail as of 2026-09-01. Nothing implements either method — see §11-16.
The Agreement Mandate MUST be an SD-JWT, following AP2's mandate pattern.
Normative: implementations MUST use a non-deterministic signature scheme (ECDSA over P-256 or stronger). Ed25519 and other deterministic schemes MUST NOT be used. (This requirement is inherited from AP2 rather than invented; its stated purpose there is to defeat rainbow-table attacks against SD-JWT disclosure salts.)
W3C Verifiable Credentials MUST NOT be assumed. (v0.1 of the design analysis described AP2 as three W3C VC mandates; that was the September-2025 launch model and is superseded
Adopted from AP2:
| AP2 | Governed Crossing | |
|---|---|---|
| Open mandate | Created before a specific transaction. Carries the user's authorization and constraints on agent behaviour. Signed by the Trusted Surface on behalf of the user. Agent's public key appears as cnf. |
Created before any specific agreement. Carries the Principal's authorization and constraints on agent behaviour: commitment classes, magnitude limits, permitted counterparties, expiry. Signed by the Principal. Agent's public key appears as cnf. |
| Closed mandate | Transaction-specific. Signed by the user (human-present) or by the shopping agent (autonomous), validated against the open mandate's constraints. | Agreement-specific. Signed by an authorized human (T2) or by the agent (T1) under the standing Open Mandate, validated against its constraints. |
| Verification | Verifiers evaluate whether a closed mandate satisfies all constraints from its corresponding open mandate before processing. | Identical. A Counterparty MUST evaluate the Closed Mandate against the Open Mandate before committing. |
A structural alignment worth recording. AP2's human-present / human-not-present distinction is isomorphic to ANAX's T2 / T1 tiers. A human-present closed mandate is a human committing — T2. An autonomous closed mandate is an agent committing within constraints its Principal signed — T1. T0 produces no mandate at all, because T0 makes no commitment.
This is not a coincidence to be noted in passing; it means the tier model has an existing, deployed cryptographic expression rather than needing one invented. It should be stated plainly in any external presentation of the standard.
Claims marked [AP2] are used with AP2's semantics. Claims marked [NEW] are additions specific to agreements and have no AP2 equivalent.
| Claim | Variant | Source | Meaning |
|---|---|---|---|
vct |
both | [AP2] | Schema version. mandate.agreement.open.1 | mandate.agreement.closed.1 |
iss |
both | [AP2] | Issuer. Open: the Principal. Closed: the human or agent signing. |
sub |
both | [NEW] | The agent the mandate authorizes. |
aud |
both | [AP2] | Open: urn:anax:va:001 — the Verification Authority, as the Registration Authority issues it and as the Mandate primitive's verifier enforces it (audience). An Open's counterparty binding is permitted_counterparties (§5.2 item 5), never aud. Closed: the Counterparty — the party that will verify and act on it (§5.4 check 7). (v0.4 — reconciles the two conventions; the earlier wording "the Counterparty, or a wildcard for an unbounded Open" described the Closed and was read against the Open.) |
cnf |
open | [AP2] | The authorized agent's public key. REQUIRED on an Open Mandate that permits autonomous (T1) closing. |
exp |
both | [AP2] | Expiry. REQUIRED on both variants. See below. |
iat |
both | [AP2] | Issued at. |
nonce |
closed | [AP2] | Replay resistance. |
sd_hash |
both | [AP2] | SD-JWT disclosure binding. |
commitment_class |
both | [NEW] | Open: the highest class permitted (L-1 — actions of class ≤ this clear). Closed: the single class asserted. Ladder below. |
commitment_act |
both | [NEW] | What act is committed, as distinct from how much is at stake. String; vocabulary is per profile/domain and is not fixed by this specification. |
magnitude_limit |
open | [NEW] | The ceiling. Non-negative integer; unit fixed by class — below. |
magnitude_asserted |
closed | [NEW] | The magnitude of this commitment — integer, same unit as the class (§4.1.3). MUST satisfy magnitude_limit. |
permitted_counterparties |
open | [NEW] | Allowlist of Principal identifiers, or an explicit wildcard. |
agreement_hash |
closed | [NEW] | Hash of the canonical Terms Document. AP2's checkout_hash analogue — but see §5.8. |
open_mandate_hash |
closed | [NEW] | Binds this Closed Mandate to the Open Mandate it was issued under. |
governance_basis |
closed | [NEW] | {pack_id, pack_version} — which governance rules were applied. |
crossing_id |
closed | [NEW] | AP2's transaction_id analogue. Resolves on the Canal chain. |
signer_role |
closed | [NEW] | human | agent. Distinguishes T2 from T1 at verification time. |
Normative requirements on claims.
exp MUST be present on both variants. A mandate that cannot lapse is not a mandate. (This is the
field the design analysis found absent from every existing ANAX authorization surface:
neither neither the admin session store nor the API-key store carries an expiry.)cnf MUST NOT be used to close a mandate autonomously; only a
signer_role: human Closed Mandate may be issued under it. (This is how a Principal restricts an agent
to T2 for a class of commitments.)permitted_counterparties MUST be present. An unbounded Open Mandate MUST state that
explicitly rather than by omission. ⚖ Fail-closed on scope — inherited from existing ANAX behaviour. Silence is refusal, never permission.magnitude_asserted MUST satisfy magnitude_limit. A verifier that cannot evaluate the comparison
MUST treat it as failed.The commitment class ladder. commitment_class is one of five fixed values, ordered. Higher classes
commit more; a Mandate's class is the highest class of action it authorises.
| Order | Value | Meaning | Examples |
|---|---|---|---|
| 1 | observe |
No effect outside the agent. Read, recommend, draft. | reading a record; producing a recommendation |
| 2 | reversible |
An effect the Principal can undo unilaterally, without a counterparty's consent, within the Mandate's exp. |
placing a hold; creating a draft record another system can delete; sending an internal message |
| 3 | irreversible |
An effect that cannot be undone unilaterally, but carries no financial or legal commitment. | sending an external email; publishing; provisioning access; deleting data |
| 4 | financial |
Moves or commits value. | payment, transfer, order, refund |
| 5 | binding |
Creates a legal obligation for the Principal. | signing, accepting terms, submitting a regulatory filing |
observe requires no Mandate under T0 (see Profile C0); it is in the ladder so that delegation and
comparison are total.commitment_act is a separate axis. commitment_class answers how much is at stake; it does not
answer what act is being committed, and the two are orthogonal rather than two spellings of one thing.
L-5 governs commitment_class alone and does not make an unrecognised commitment_act malformed; the act is
bounded by the profile's act vocabulary at the seal (§5.4 check 9, v0.4.4), not by L-5.
magnitude_limit — a non-negative integer whose unit is fixed by the class:
| Class | Unit of magnitude_limit |
Example |
|---|---|---|
observe |
count of read operations (0 = unlimited within exp) |
0 |
reversible |
count of actions | 50 |
irreversible |
count of actions | 5 |
financial |
minor currency units (cents/pence), with currency REQUIRED alongside (ISO 4217) |
250000 + "currency":"EUR" = €2,500.00 |
binding |
count of instruments (1 = exactly one signature/filing) | 1 |
financial, currency is a required sibling field; a financial Mandate without it is
malformed. A Mandate MUST NOT mix currencies; delegation across currencies is a new issuance, not a
narrowing.financial → irreversible) resets the unit; the
delegated magnitude is then bounded by L-2 only.magnitude_limit bounds the total committed under the Mandate over its lifetime,
not per action. Effectors MUST track consumption per Mandate; a crossing that would exceed the
remaining magnitude is refused, not partially executed.magnitude_limit = d under an Open Mandate O consumes d of O's remaining magnitude at
issuance. The issuer MUST refuse a Closed whose draw exceeds O's remaining (MAGNITUDE_EXHAUSTED at
issuance). The sum of the draws of every Closed ever issued under O therefore never exceeds O's magnitude_limit:
M-4 holds by construction, and a seal does not touch O. The Open is the per-agent envelope; the Closed is the
per-agreement permission.crossing:seal consumes the
initiator's Closed and the responder's Closed, each in full (consumed:= magnitude_limit), each by presentation of
its exact bytes to the issuing authority (§3.7.2 effector rule). A Closed already consumed is refused
MAGNITUDE_EXHAUSTED. A Closed that expires unsealed leaves its draw booked against O; no release exists at v1
(§11-17) and O's own exp bounds the loss. (The earlier reading in which the seal debits the Open is withdrawn: it made every seal a remote read-modify-write of the envelope and left the sum of
outstanding draws unbounded between issuance and seal.)0 means "no actions of this class" for classes 2–5, and "unlimited reads within exp" for
observe only.⚠ A Mandate is permission to ATTEMPT, not a guarantee of completion — note for v0.4. Consumption is
recorded BEFORE the act it authorises, not after, and a unit spent on an attempt that then fails downstream
is spent. This is deliberate and it is the only order that makes magnitude_limit a control: a counter
decremented after the act has already happened is an audit trail, not a brake, and an effector that
consumed on success alone would grant itself unlimited retries of an act it was authorised to perform a
bounded number of times. The cost is that an attempt lost to an unrelated outage still counts against the
grant. Implementations SHOULD emit a distinguishable event when this occurs — the reference deployment logs
mandate_unit_burned_on_503 — so the loss is measured rather than silently absorbed into the count.
Conformance vectors — series CM-.
irreversible, magnitude 5: five irreversible actions clear; the sixth is refused with reason magnitude_exhausted.reversible: an irreversible action is refused with reason class_exceeded.financial/250000 EUR → child financial/300000 EUR is rejected at issuance (narrowing_violation); child financial/100000 EUR is accepted.financial/250000 EUR → child financial/100000 GBP is rejected (currency_mismatch).finance (unknown value) is rejected as malformed.financial Mandate without currency is rejected as malformed.⚠ v0.4 note (2026-09-02): measured against the components that hold each rule — the Registration Authority for CM-3…CM-6 and the responder's crossing-path seam for CM-1/CM-2 — CM-1, CM-2, CM-3, CM-5 pass; CM-4 and CM-6 are conditional on M-TRANSACT being activated, because they exercise financial, which the operator's charter makes unreachable by design; CM-3 is restated at irreversible (see the A-2 addendum's v0.4 note). The sentence this replaces — all fail as of 2026-09-01 — was true of the harness that day and is retained in the change log. §11-16 still stands for the WIRE contract: WC-1…WC-7 remain unimplemented.
Relationship to Blast Radius. Blast Radius (the Authority Doctrine, forthcoming, §2.2) aggregates magnitude_limit
per class over reachable effectors. This vocabulary makes that aggregation well-defined: sums are within a
class, and within a currency for financial; the ladder gives the weighting order. (OQ-A1 answered: fixed
ladder. A weight table for a single scalar exposure figure is deferred to Authority Doctrine v0.2 — this
specification fixes vocabulary, not weights.)
⚠ This section said "the Mandate does not exist" until 2026-09-01. It does now. The Mandate primitive was built and exercised on that date, and the claim is recorded here with what was measured rather than what was intended:
M-READ Mandate authorises observe
and refuses reversible. This is the property the row below says the prior half-implementations could
not have, and it is the reason the signed open/closed pattern was adopted.magnitude_limit is enforced atomically at the row (SELECT … FOR UPDATE), and a first effector consults it before acting: over 40 authenticated reads on a governed route,
27 were authorised and 13 refused, with the refusals carrying magnitude_exhausted and the read
demonstrably not performed.⚠ WHAT THIS DOES NOT SAY. One effector, on one route, in one service. §4.1.3's rules still have no general enforcing component: CM-1…CM-6 remain 6 of 6 failing and §11-16 stands. (v0.4, 2026-09-09: both counts have since moved — CM-1…CM-6 are 4 pass / 2 conditional / 0 fail, and the §3.7 methods are implemented and live. The sentence is retained as the record of its date; the current measurement is the v0.4 note in §11-16, which also lists the three things still not enforced.) Delegation narrowing is enforced at the issuer only. A Mandate existing is not the same as this specification being implemented, and the distance between those two statements is exactly what §11 lists.
The prior state, retained — what existed before the Mandate primitive, and why neither half was a Mandate:
| Field | Nearest existing thing | Why it is not a Mandate |
|---|---|---|
| principal | an admin session record / an API-key record | a person's email, or a bearer secret |
| scope | the API key's scope list | present on the machine side only |
| magnitude | tier constants | hardcoded constants, not issued authority |
| expiry | — | absent everywhere |
| commitment class | — | absent everywhere in code. The vocabulary is now fixed (§4.1.3) and nothing enforces it (§11-16) |
| verifiability | — | both are bearer secrets. A bearer secret proves possession, not authority, and cannot be shown to a counterparty. |
That last row is why AP2's signed open/closed pattern is adopted rather than extended from what exists.
AP2's mandates originate with a natural person, signed by a Trusted Surface on their behalf. A Governed Crossing Principal is an organization, and a T1 commitment may involve no human at all. The question "who may sign an Open Agreement Mandate for an organization?" therefore has no AP2 answer and must be ANAX's.
It is answered once, by the Mandate primitive — and the same answer serves the question of who may bind the organization.
This is not a coincidence to be tidied away; it is the reason the design analysis names the Mandate as a primitive rather than a protocol field. The same object answers three questions that look separate:
| Question | Asked by |
|---|---|
| What may this agent commit to, and up to what magnitude? | Tiered autonomy, the design analysis |
| How does a counterparty verify that authority? | This specification, §4.1 |
| Who may bind the company — for humans and agents alike? | the organizational control-map question |
Normative consequence for this specification: the identity and authority of an Open Mandate's iss
MUST be resolvable to the Principal by the Counterparty, and an implementation MUST NOT accept an
Open Mandate whose issuer it cannot resolve to the claimed Principal (§5.3, failure path
counterparty unverifiable). How that resolution works — a delegation chain, a registry, a signed
Agent Card lineage — is the Mandate primitive's design and is IMPLEMENTATION PENDING there, not here.
This specification states the requirement and consumes the answer; it does not fork it. Forking it is
precisely how one mandate model becomes two, which is the state the design analysis found and is closing.
agreement_hashADOPTED 2026-09-01 — RFC 8785 (JCS). The adoption gate is passed; see §5.8 for how.
AP2's checkout_hash binds a mandate to a merchant-issued Checkout JWT — a third artifact, produced by
one party, with a canonical serialization the protocol gets for free. A bilateral agreement has no
such artifact. The Terms Document is jointly constructed, and if the two parties canonicalize it
differently they will compute different hashes and the crossing will fail — or worse, appear to succeed
against different documents.
Normative requirements:
agreement_hash MUST be computed as SHA-256( JCS(terms_document) ), where JCS is the JSON
Canonicalization Scheme of RFC 8785, applied to the Terms Document as a JSON value, producing a
UTF-8 byte sequence. The digest is rendered as 64 lowercase hexadecimal characters.Constraints on Terms Documents, so canonicalisation is unambiguous:
parties arrays differ
only in order are different documents.terms_version (string). Implementations reject documents without it.Conformance vectors — series TD-. TD-1 minimal; TD-2 the same document with keys shuffled and
whitespace added (MUST equal TD-1); TD-3 nested object with a non-ASCII string, emitted as UTF-8 rather
than \u escapes; TD-4 array order significant (MUST differ from TD-1); TD-5 booleans and null. An
implementation claiming conformance MUST reproduce all five byte-for-byte.
JCS is chosen over an ANAX-internal utility for one reason that outweighs familiarity: a bilateral hash must be computable by an implementer who has never seen ANAX code, from a published standard, in the language of their choice.
the existing single-party serialiser is noted as the current single-party
utility, not the answer. It has the right instinct — deterministic key ordering, with an explicit
?? null discipline so an absent key and a null key stay distinguishable — but it is one implementation in
one repository with no published test vectors, and it was written for a basis constructed by one party.
⚖ Adoption gate — PASSED 2026-08-31. The gate was: published test vectors, and two independent implementations producing byte-identical hashes over them. Both halves are met; §5.8 records exactly how, so a reader can check the claim rather than take it. Requirement 2 — both parties compute independently, mismatch MUST abort before sealing — stands regardless, and is what protects the crossing when an implementation is wrong.
The Crossing Seal attaches to Artifact.metadata, on the Artifact representing the agreement, and is
echoed into Task.metadata on the terminal Task.Task, Message and Artifact each carry a metadata field, typed as a map of string to JSON values.A2A's extension constraints are explicit: an extension may not change the definition of core data
structures and may not add values to enum types; "custom attributes belong in the metadata map
present on core data structures", and the Timestamp extension's documented example places its data on
Message and Artifact.
Why the Artifact and not the Message: Artifacts are the durable outputs A2A expects to be retained and referenced; a Message is transient. A seal that lives only on a transient object cannot serve post-hoc verification, which is the entire purpose (§6).
Message and Artifact additionally carry an extensions field. The extensions
documentation does not describe its relationship to metadata. The division is that extensions declares which extension URIs apply to the object and metadata
carries the data. CONFIRMED 2026-09-01 against the normative A2A definition:
the A2A protocol buffers definition declares repeated string extensions = 7 on Message ("the URIs of extensions
that are present or contributed to this Message") and repeated string extensions = 6 on Artifact
("…to this Artifact"), and both objects also carry metadata ("optional metadata for extensions; the key is
an extension-specific identifier"). A2A v0.3.0 §6.4 and §6.7 give extensions?: string[] on both. The
division above is the A2A design, not an inference. This specification populates both.
Artifact.extensions = ["https://anaxstandard.ai/ext/governed-crossing/v1"]
Artifact.metadata["https://anaxstandard.ai/ext/governed-crossing/v1"] = {
"crossing_state": "committed", // extension-scoped. NOT a TaskState — see §4.5
"seal_id": "canal-<ts>-<rand>",
"seal": "<sha256 hex>", // over the Bilateral Record, §4.4
"prev_hash": "<sha256 hex | null>",
"chain_seq": 42,
"is_genesis": false,
"seal_version": 3, //
"pack_id": "<pack>", // governance basis of the sealing party
"pack_version": "1.2.0",
"outcome": "CLEARED", // the outcome vocabulary
"sealed_at": "<RFC3339>",
"verify_url": "https://verify.anaxstandard.ai/?id=canal-<ts>-<rand>"
}
Every field in that block already exists in the current implementation. The projection an A2A counterparty needs is the projection
GET /canal/verify/:seal_id already computes — including its deliberate stripping of clearance_id,
the envelope access key, while retaining chain_seq, is_genesis and prev_hash as non-sensitive
position context.
Normative requirements.
clearance_id MUST NOT appear in Artifact.metadata. It is an access key, not position context.verify_url MUST point at the Verification Authority (§2.2) and MUST use .ai.The object the Crossing Seal is computed over. IMPLEMENTATION PENDING — this is the Bilateral Crossing,
and it does not exist: today's Canal Crossing carries one ai_system, an owner reference,
and no counterparty field anywhere in the contract, the transaction
store, or the chain.
{
"record": "anax-bilateral-crossing-v1",
"crossing_id": "<uuid>",
"created_at": "<RFC3339>",
"parties": [ // exactly 2 — see normative note
{
"role": "initiator", // "initiator" | "responder"
"principal_id": "urn:anax:principal:<opaque>",
"agent_id": "<A2A agent identity>",
"clearance_id_hash": "<sha256>", // HASH, never the key itself
"trust_basis": "vicca_authority_sealed",
"agc_level": 3,
"open_mandate_hash": "<sha256>",
"closed_mandate_hash": "<sha256>",
"signer_role": "agent", // "human" | "agent" — T2 | T1
"governance_basis": { "pack_id": "<pack>", "pack_version": "1.2.0" }
}
//... responder
],
"agreement_hash": "<sha256>", // §4.2 — SHA-256(JCS(terms))
"commitment_class": "<class>", // the §4.1.3 ladder: observe|reversible|irreversible|financial|binding
"commitment_act": "<act>", // what act; per-profile vocabulary, §4.1.3
"magnitude_asserted": 250000, // integer, unit fixed by class (§4.1.3)
"currency": "EUR", // REQUIRED when commitment_class is financial (M-2)
"a2a_context": {
"task_id": "<A2A Task.id>",
"context_id": "<A2A Task.contextId>",
"artifact_id": "<A2A Artifact.artifactId>"
},
"timestamps": {
"mandates_verified_at": "<RFC3339>",
"terms_agreed_at": "<RFC3339>",
"sealed_at": "<RFC3339>"
},
"prev_hash": "<sha256 | null>", // chain position, sealed IN — see below
"chain_seq": 42
}
Normative requirements.
parties MUST contain exactly two entries, with distinct principal_id values and exactly one of
each role. (Multilateral crossings are out of scope for v1 and are noted as an open problem, not an
omission.)clearance_id_hash MUST be a hash. The raw clearance_id is an envelope access key and MUST NOT
appear in the record.prev_hash values, and a bilateral crossing has two clearance-envelope chains. The value is
therefore split:joint_seal = SHA-256( canonical(Bilateral Record, chain position EXCLUDED) ) // party-independent
anchor_i = SHA-256( joint_seal ‖ prev_hash_i ‖ chain_seq_i ) // one per party
// ‖ is NORMATIVE from v0.4 (adopted 2026-09-05):
// anchor_i = SHA-256( UTF-8( `${joint_seal}|${prev_hash ?? "genesis"}|${chain_seq}` ) )
// "|" is the literal 0x7C; genesis prev_hash is the string "genesis"; chain_seq is decimal, unpadded.
// Vectors AN-1 (genesis) and AN-2 (non-genesis) are published below; a second implementer MUST match both.
Conformance vectors — series AN- (folded into this section at v0.4.3; previously in an internal
addendum). Fixtures are themselves SHA-256 of fixed strings so anyone can regenerate them:
joint_seal = SHA-256("anax-an-vector:joint_seal"), prev_hash = SHA-256("anax-an-vector:prev_hash").
| Vector | prev_hash |
chain_seq |
Preimage | anchor |
|---|---|---|---|---|
| AN-1 genesis | null |
0 |
e2cd3bbd…f270|genesis|0 |
0389b62946981d1155cb6ab1b70ad99f15f3f9b66cccdbdb477976948842d85e |
| AN-2 non-genesis | 293e3037…4012 |
42 |
e2cd3bbd…f270|293e3037…4012|42 |
3d907220b49faf996328fe47102d8941929e38cf9232026349b54dcdfe53e2cd |
Full fixture values: joint_seal = e2cd3bbdb71cbf699b3b554a7081c7a24e695c923437906b55fbc67aba30f270,
prev_hash = 293e3037ba29ad930f1b9357aaff94d41e075a06c50efa99a9c40ee3f4a44012.
joint_seal. It is the commitment.anchor into its own clearance-envelope chain,
preserving the existing property that a chain link commits to its own position.joint_seal is the value published, distributed (§5.6) and resolved at the Verification Authority
(§6.2). anchor_i is each party's internal chain evidence and is not the identifier of the
commitment.joint_seal was computed and agreed
by both parties (§5.5) and at least one anchor was written, the commitment exists — because a
commitment is defined by resolvability, not by message or by write count (§5.6). A missing anchor is
an audit-trail gap for that party, recoverable by an idempotent retry (§5.6 req 3), and the party
holding it MUST retry rather than treat the commitment as absent. This is distinct from F6
(§5.7), which is the case where no anchor could be written and no chain position exists at all.p in parties: p.agent_id MUST be the
agent subject (sub) of the Mandate at p.closed_mandate_hash, and that Mandate's open_mandate_hash MUST
equal p.open_mandate_hash. For an ANAX-derived subject, p.agent_id is urn:anax:agent:<sha256(lowercased owner)> and no other form. A record whose party does not satisfy this is not the record of that party's
commitment. (Why: without it, any holder of a valid Closed Mandate for the agreement could seal as a party it
is not — the hash in the record is whatever the initiator wrote. Req 7 makes the presented bytes, the record's
hash, and the record's named agent one fact. principal_id stays opaque; agent_id is the join.)Today's chain is keyed per clearance envelope: one chain per clearance envelope, unique on (envelope, sequence), and the seal is computed over a basis that includes prev_hash. A bilateral crossing has two envelopes, and therefore two possible chain positions
with two different prev_hash values.
⚠ This surfaces a genuine tension in §4.4 requirement 4. A seal cannot simultaneously commit to two different
prev_hashvalues. Any option that anchors in both chains requires splitting one value into two levels.✅ RESOLVED (2026-08-09): Option A — dual anchoring — is APPROVED. §4.4 requirement 4 is restated above in terms of the two-level split. The options analysis below is retained as the record of why.
The two-level split (required by Options A and D, not by B or C):
joint_seal = SHA-256( canonical(Bilateral Record without chain position) ) // party-independent
anchor_i = SHA-256( joint_seal ‖ prev_hash_i ‖ chain_seq_i ) // one per party
Both parties compute an identical joint_seal; each computes its own anchor. The existing property —
a seal that commits to its own chain position — is preserved at the anchor level, and the new property —
a seal both parties can independently derive — is satisfied at the joint level.
| Option | How it works | For | Against | |
|---|---|---|---|---|
| A | Dual anchoring (recommended) | Each party appends to its own envelope chain; each anchor references the shared joint_seal; mutual cross-reference recorded in the Bilateral Record. |
Each party's own chain stays a complete record of its own commitments. Neither party depends on the other's infrastructure for its own evidence. Existing per-chain contention logic works unchanged — each anchor is an ordinary single-chain append. | Requires the two-level split, i.e. reopens §4.4 req 4. Two writes, so a partial-write window exists (§5.6). |
| B | Single designated chain | The initiator anchors; the responder holds a reference only. | Simplest. Preserves every existing invariant with no change. One write, no partial-write window. | Asymmetric. The responder's own chain has a hole where its commitment should be, and it depends on the counterparty's infrastructure for its own audit trail. For a standard whose premise is that neither party need trust the other, this is the wrong shape. |
| C | Third joint record | A new chain keyed by crossing_id, independent of both envelopes. |
Clean symmetry; no two-level split needed. | Neither party's own envelope chain shows its bilateral commitments, so a party auditing itself misses them. Needs a new UNIQUE key and new contention logic — the existing hardened path is not reused. |
| D | Hybrid (C + A) | Joint record on its own chain, plus per-party anchors referencing it. | Most complete. Both the joint view and each party's self-audit are whole. | Most work; three writes; two new failure modes to specify. Hard to justify before a first implementation exists. |
✅ DECIDED: Option A — dual anchoring. The decisive fact is that the per-envelope audit endpoints already exist and are already per-envelope: an authenticated owner-scoped verify endpoint and its export counterpart. Under Option B or C, a party exporting its own chain for a regulator, insurer or court would produce a record with its bilateral commitments missing — which defeats the purpose those endpoints exist for. Option A preserves them, at roughly a third of the cost of D.
Option D remains additively reachable, and its natural future host is named. A joint chain can be
added on top of Option A without migration: the joint_seal is already party-independent, so a central
registry consumes exactly the value both parties already compute, and no existing anchor, chain or exported
record changes. That registry's natural home is the central crossing registry of the FRYKTORIA Centre
(roadmap). Recorded here so that the decision to start with A is understood as a first step rather than a
ceiling.
Consequences applied in this draft:
joint_seal / anchor_i), including
the partial-anchor-write rule.A party's anchors for bilateral crossings are not one undifferentiated chain. They are scoped per
counterparty pair, so that no relationship leaks into another through prev_hash.
Normative.
chain_id for a crossing is a deterministic function of the two parties' agent subjects (§4.4 req 7
agent_id), independent of role and order: chain_id = SHA-256( UTF-8( min(agent_id_A, agent_id_B) || "|" || max(agent_id_A, agent_id_B) ) ), where min/max are by byte-wise (code-point) comparison of the UTF-8
strings. Both parties compute the same chain_id for the same pair.chain_id: chain_seq starts at 0 at the first crossing of the pair
(prev_hash = null, the genesis of that relationship — the §4.4 anchor rule hashes it as the literal genesis) and increases by
one per sealed crossing of the same pair. (chain_id, chain_seq) is unique per party.prev_hash MUST be the previous anchor of the same chain_id and nothing else. An anchor that references a
prev_hash from a different pair's chain is malformed (CHAIN_POSITION_UNAVAILABLE at seal; chain_position_valid: false at verify). Consequently a verifier holding one relationship's chain learns nothing about the existence,
count or timing of any other relationship of either party.crossing:verify reports chain_id alongside prev_hash, chain_seq, is_genesis (§3.7.3); it does not report
the subjects the id was derived from.(Why: a single chain per party would make every counterparty a witness to every other — the previous anchor of
a crossing with B would be the prev_hash in a record shared with C. Envelope chains (§4.4.1) are already scoped
per clearance_id; this is the same discipline for the bilateral case. The cost — a genesis per relationship — is
no cost: genesis is a value, not a ceremony.)
TaskStateThis is a forced deviation from an earlier draft.TaskState is a closed enumeration of eight values: TASK_STATE_SUBMITTED, TASK_STATE_WORKING,
TASK_STATE_COMPLETED, TASK_STATE_FAILED, TASK_STATE_CANCELED, TASK_STATE_REJECTED,
TASK_STATE_INPUT_REQUIRED, TASK_STATE_AUTH_REQUIRED.Extensions may not add new values to enum types.
the design analysis proposed "a State Machine extension adding a committed state". That proposal
is not conformant as literally stated — and it is precisely the trap the design analysis itself warned about at
§B4.1, now confirmed against the actual enum rather than anticipated. The resolution:
The core
TaskStateremains one of the eight. Commitment is an extension-scoped state, carried inTask.metadataunder the extension URI.
| Extension state | Core TaskState |
Meaning |
|---|---|---|
mandates_pending |
TASK_STATE_AUTH_REQUIRED |
Counterparty must present mandates. (A near-exact fit — this core state exists for precisely this shape of requirement.) |
terms_proposed |
TASK_STATE_INPUT_REQUIRED |
Terms on the table, awaiting counter-signature. |
sealing |
TASK_STATE_WORKING |
Mandates verified; seal being written. |
committed |
TASK_STATE_COMPLETED |
Sealed. The Crossing Seal exists and resolves. |
refused |
TASK_STATE_REJECTED |
A normative refusal — expired mandate, magnitude exceeded, counterparty unverifiable. |
failed_unsealed |
TASK_STATE_FAILED |
Seal could not be written. No commitment was made. |
⚖ Normative, and it is the most important rule in §4:
A verifier MUST NOT infer commitment from TaskState alone. TASK_STATE_COMPLETED on a Governed
Crossing task means the task finished — it does not mean a commitment was sealed. Only a resolvable
Crossing Seal proves a commitment. An implementation that treats TASK_STATE_COMPLETED as commitment has
implemented a downgrade attack against itself.The mapping above is a genuine gain rather than a workaround. TASK_STATE_AUTH_REQUIRED and
TASK_STATE_REJECTED already exist and already mean approximately the right thing, so the failure paths
land on core states that unextended A2A agents can interpret. An agent that does not implement Governed
Crossing still sees "this task was rejected" — the correct impression — rather than an opaque state.
Five phases. Each MUST complete before the next begins; there is no pipelining, and in particular no phase may begin on the strength of a predicted outcome of an earlier one.
P0 Discovery & activation Agent Cards read, extension activated (§3.5)
│
P1 Mandate exchange Open Mandates exchanged and screened (§5.2)
│
P2 Terms proposal Terms Document converged; agreement_hash (§5.3)
│ computed INDEPENDENTLY by both parties
│
P3 Mutual mandate verification Closed Mandates issued, exchanged, and each (§5.4)
│ verified against its own Open Mandate
│
P4 Joint sealing Bilateral Record built; joint seal computed (§5.5)
│ by both; chain write(s) performed
│
P5 Seal distribution Seal attached to Artifact; both parties (§5.6)
confirm resolvability at the VA
Extension states (§4.5) advance mandates_pending → terms_proposed → sealing → committed, with
refused and failed_unsealed as terminal alternatives.
⚖ Governing rule for every phase: refuse, never degrade. Inherited from existing ANAX behaviour. At no point in this flow is a partial success an acceptable outcome. A commitment is made or it is not.
Each party sends its Open Agreement Mandate in Message.metadata under the extension URI.
Screening verification. On receipt, a party MUST check, before proceeding:
vct is mandate.agreement.open.1.exp is in the future.iss resolves to the claimed Principal (§4.1.5).permitted_counterparties includes this party, or explicitly states unbounded. (v0.4 note: at
crossing v1 this claim is issued by nobody and verified by nobody, so item 5 is NOT enforced by the
reference implementation. Accepted for v1 because the two bindings that matter already hold elsewhere:
the Closed's aud names the counterparty (§5.4 check 7) and §5.4 check 9 binds each party's Mandates
to the record by hash and subject. An Open's aud is the Verification Authority (§4.1.3) and is not a
counterparty check; a responder MUST NOT refuse an Open on its aud. Hardening pass: issue the claim on
Opens, verify it here.)commitment_class overlaps with the class this crossing concerns.cnf is present.Failure at P1 MUST terminate the crossing. A party MUST NOT proceed to negotiate terms it cannot lawfully be offered. Screening here is not an optimisation — negotiating first and checking authority afterwards is how a party ends up holding an agreement it knew, at the time it was made, could not be honoured.
Normative: a party MUST NOT disclose its magnitude_limit beyond what permitted_counterparties
and commitment_class require.A counterparty that learns the exact ceiling learns exactly what to
ask for; this is the pressure-negotiation surface of §7.5, and the mitigation begins here, at disclosure,
not later at refusal.
The parties converge on a Terms Document (§4.2). This specification does not govern negotiation content; it governs what happens to the result.
Normative:
agreement_hash over the canonical serialization.Aborting on mismatch is not defensive tidiness. Two parties who seal against different documents have produced a record that proves a commitment neither of them made — the exact failure a Crossing Seal exists to make impossible.
Each party issues a Closed Agreement Mandate binding agreement_hash, open_mandate_hash,
magnitude_asserted, crossing_id, nonce and signer_role, and sends it to the counterparty.
Binding verification. Each party MUST verify the counterparty's Closed Mandate against the counterparty's Open Mandate:
signer_role is agent, the signing key MUST match the Open Mandate's cnf.open_mandate_hash matches the Open Mandate received at P1.agreement_hash matches the value agreed at P2.commitment_class is one of the classes the Open Mandate permits.magnitude_asserted satisfies magnitude_limit (§4.1.3) — one integer against one integer, in the unit the
class fixes. (v0.4: magnitude_asserted is record-level, one per crossing; it MUST satisfy
both parties' Closed magnitude_limit and, through them, both Opens' declared limits — conjunctive
across parties.) (v0.4.2: the magnitude is a single integer on the record and on each Closed Mandate
(§4.1.2); the earlier "every dimension" wording described a per-dimension object no Mandate carries in
this specification and is withdrawn.)exp on both mandates is in the future at the moment of verification, not merely at issuance.aud names this party — the Closed Mandate's aud. (v0.4: the Open's aud is the Verification
Authority and is not checked here or at P1.)nonce has not been seen before for this crossing_id.SHA-256(initiator_closed_mandate) == parties[initiator].closed_mandate_hash,
closed.sub == parties[initiator].agent_id, closed.open_mandate_hash == parties[initiator].open_mandate_hash,
closed.commitment_act == record.commitment_act;
and the same for the responder against its own stored Closed Mandate and parties[responder].
Failure on either party is MANDATE_PARTY_MISMATCH (§3.7.4). This check is pure verification and precedes any
consumption.
(v0.4.4, normative) A Closed Mandate MUST name the act it is drawn for (commitment_act, §4.1.3); the
responder MUST refuse, as MANDATE_PARTY_MISMATCH, a Closed whose act is absent or differs from the
record's, and, as MALFORMED_MANDATE, a record whose act is outside the profile's act vocabulary (§4.1.3).Magnitude equality (v0.4.2, normative). The record's magnitude_asserted MUST equal the asserted
magnitude of each party's Closed Mandate (§4.1.2 magnitude_asserted); a mismatch is refused
MAGNITUDE_MISMATCH (§3.7.4). This is an integrity check distinct from the budget ceiling
(MAGNITUDE_EXHAUSTED): the former means two sealed documents disagree on the amount, the latter means the
amount exceeds the Mandate. It is pure verification and precedes any consumption.
Revocation at commit (v0.4.1, normative). A Mandate that was active when it was consumed may be
revoked before the seal is written. The responder MUST re-read the status of both Closed Mandates
inside the transaction that writes the seal, holding a read lock on both rows so that a concurrent
revocation either lands before the read or waits for the write; if either Mandate is no longer active
the seal MUST be refused MANDATE_REVOKED (§3.7.4), nothing is written, and the refusal is recorded
with both consumptions marked as spent (§11-17, cost (b)). This is the bilateral form of the Mandate Object
Specification (forthcoming) §2.4 (a revoked Mandate fails at commit even if it passed at start). The re-read is a read:
consumption remains the Registration Authority's alone.
Normative: a verifier that cannot evaluate any check MUST treat it as failed. There is no "unable to determine, therefore accept" branch anywhere in this specification. (This mirrors the fail-closed discipline already in the reference implementation: an empty scope list denies rather than permits.)
Normative:
anchor into its own clearance-envelope chain (§4.4 req 4, §4.4.1
Option A). The joint_seal is shared and identical; the anchors are per-party. A party MUST NOT
write an anchor before seal comparison (req 2) has succeeded.failed_unsealed (core TASK_STATE_FAILED) and MUST NOT act on the agreement. Retry is permitted;
silent acceptance is not.The Crossing Seal is attached to Artifact.metadata on the Artifact representing the agreement, and echoed
into Task.metadata on the terminal Task (§4.3).
⚠ Open problem, stated rather than papered over. Between the chain write and the counterparty's receipt of the seal there is a window in which one party knows a commitment exists and the other does not. This is the two-generals problem and it is not solvable by adding another acknowledgement.
The mitigation is definitional, and it is the right one:
⚖ A commitment is defined as resolvable at the Verification Authority, not as delivered by message.
Consequences, all normative:
crossing_id at the Verification Authority (§6) before acting on that assumption.crossing:seal MUST be idempotent on crossing_id. A retry after a lost response MUST return
the existing seal, never mint a second one. (The estate already holds this discipline: the edu passport
id is derived from the submission precisely so that a queue retry is a no-op by construction, after a
defect in which a retry wrote a second attestation for one assessment. A second attestation of one
commitment is the same failure at protocol scale.)Every path below is a normative refusal, not an error to be recovered from by proceeding. In each,
refused maps to core TASK_STATE_REJECTED (§4.5), except where noted.
| # | Failure | Detected at | Normative behaviour |
|---|---|---|---|
| F1 | Expired mandate — exp on an Open or Closed Mandate is past at verification time |
P1 screening; re-checked at P3 | MUST reject. MUST NOT commit. A mandate valid at P1 and expired at P3 MUST fail: expiry is evaluated at the moment of verification, never at issuance. The counterparty MAY be invited to present a re-issued mandate, which restarts at P1. |
| F2 | Magnitude exceeded — magnitude_asserted exceeds magnitude_limit |
P3 | MUST reject. MUST NOT commit. The refusal is structural, not discretionary: there is no override, no "approve anyway", and no human-in-the-loop path that rescues this mandate. The correct remedy is a new Open Mandate from the Principal, or a T2 human-signed Closed Mandate if the Principal's governance permits one for this class. |
| F3 | Counterparty unverifiable — iss does not resolve to the claimed Principal, signature fails, or cnf does not match the signing key |
P1 or P3 | MUST reject. MUST NOT commit. A party MUST NOT proceed on a self-asserted identity. (This is the §2.3 distinction made operational: a claim about oneself is not a verified fact about oneself.) |
| F4 | Canonicalization mismatch — the two computed agreement_hash values differ |
P2 | MUST abort before sealing. Neither party may adopt the other's value or tie-break. Both MUST treat the terms as not agreed. |
| F5 | Downgrade attempt — a commitment is requested or presented without extension activation or without a valid Crossing Seal | P0, or on any inbound commitment | MUST reject (§3.5.1). The refusal MUST be recorded and MUST be auditable (§3.5.2 req 4). A downgrade attempt is a detectable violation, and detection that is not recorded is detection thrown away. |
| F6 | Seal unwritable — chain write fails or chain position cannot be established | P4 | MUST NOT commit. State is failed_unsealed → core TASK_STATE_FAILED (not REJECTED: nothing was refused, something failed). Retry permitted; idempotent on crossing_id (§5.6). |
| F7 | Seal mismatch — the parties' independently computed joint seals differ | P4 | MUST abort. Same discipline as F4. |
Normative on refusal messaging. A refusal MUST state which failure path fired, at a granularity
sufficient for the counterparty to correct a legitimate error. It MUST NOT disclose the refusing
party's magnitude_limit, the contents of its Open Mandate beyond what was already exchanged, or any
internal governance verdict. (The estate holds the analogous rule already, and holds it deliberately:
unknown, revoked and expired engagement codes are distinct internally and identical externally, because
distinguishing them "turns this into an enumeration oracle for anyone holding a list of guesses". F2 is exactly that shape: a counterparty that can
probe until refusal stops has measured the ceiling.)
Terms canonicalization — CLOSED. Normative algorithm: RFC 8785, JSON Canonicalization Scheme (JCS). Adopted; §4.2. Not the answer: the existing single-party serialiser — the current single-party utility. Right instinct, one implementation, no published test vectors, and written for a basis constructed by one party. It remains in use for the unilateral Canal seal and is not the bilateral algorithm. ⚖ GATE PASSED 2026-08-31: vectors TD-1…TD-5 produced by a sort-keys / UTF-8 serialiser and reproduced byte-for-byte by an independent RFC 8785 implementation, with a negative control; two implementations, not one run twice. The negative control moved TD-1's digest from
cccd1864…to2b82efb6…on a single-integer change, so the comparison is known to bite rather than merely to pass. The verifier reads its expected values out of the published vector set rather than restating them, so a transcription error cannot present as conformance. Standing rule, independent of the algorithm: both parties compute independently; mismatch MUST abort before sealing (§5.3).
Rationale for preferring a published standard over an internal utility: a bilateral hash must be computable by an implementer who has never seen ANAX code, from a specification, in a language of their choosing. An internal utility cannot meet that bar however correct it is.
A Crossing Seal is only worth what a third party can do with it. This section defines the resolution path for a counterparty, and for the audiences the design analysis names: regulators, insurers, courts.
The Verification Authority is verify.anaxstandard.ai (§2.2). Seal resolution is delegated to the
Canal projection:
GET /canal/verify/:seal_id → public, unauthenticated
``` This endpoint already exists and already computes very close to what a
counterparty needs. Specifically, it **strips `clearance_id`** — the envelope access key — from both the
chain position and the returned basis, while **retaining `chain_seq`, `is_genesis` and `prev_hash`** as
non-sensitive position context. A richer authenticated owner-scoped projection
exists separately and refuses on owner mismatch.That split — public position without the key, private detail behind ownership — is exactly the
projection this specification needs, and it was built before this specification existed. It is the single
most transferable asset from the current implementation.
**What a verifier MUST check** on resolving a seal:
1. The seal resolves at all, and to the `crossing_id` in question.
2. The returned `seal` value equals the value the verifier computes from the Bilateral Record, if it holds
one.
3. `prev_hash` and `chain_seq` are present and consistent with the chain's own verification endpoint
(an authenticated owner-scoped endpoint, available to a party for its own
chain).
4. `pack_id` / `pack_version` are present — the governance basis under which the commitment was judged.
5. The seal is not marked superseded.
**Normative:** a verifier **MUST NOT** treat a non-resolving seal as merely absent evidence. Under §5.6 a
commitment **is** its resolvability; a seal that does not resolve is a commitment that was not made.
**⚠ Note on the portal's input handling.** The passport portal performs **no prefix validation** — a
`canal-` prefixed input routes to the Canal API, a UUID-shaped input is looked up on `id`, and **anything
else is looked up verbatim**. This is convenient today and is
an attack surface tomorrow; it is logged in the internal security backlog and addressed at
§7.6.
### 6.3 Offline verification
Offline verification is **partial by nature**, and the partition matters:
| Property | Offline? | Why |
|---|---|---|
| Mandate signatures validate | **Yes**, given the issuer's public key | SD-JWT verification is self-contained. |
| `agreement_hash` matches the Terms Document | **Yes**, given the document | A hash recomputation. |
| Joint seal matches the Bilateral Record | **Yes**, given the record | A hash recomputation. |
| Closed Mandate satisfies its Open Mandate | **Yes**, given both | Constraint evaluation, no network. |
| **Chain position is genuine** | **No** | Requires the chain. `prev_hash` in hand proves only what was claimed at seal time. |
| **The mandate has not been revoked** | **No** | Revocation is state, and state is not in a JWT. See §7.2. |
| **The seal has not been superseded** | **No** | Same reason. |
⚖ **Normative:** offline verification establishes **integrity**, never **currency**. An implementation
**MUST NOT** describe an offline check as verification without qualification, and a party relying on one
**MUST** state that it did so.The honest formulation for a report or an audit is: *"internally
consistent as of the record held; not checked against the ledger."*
This is not a weakness peculiar to Governed Crossing — it is the same limit every offline credential check
has — but a governance specification that let an implementer blur it would be failing at precisely the
thing it exists to do.
---
## 7. Security Considerations
Each threat is mapped to a **mitigation** or is named as an **open problem**. Open problems are stated
plainly; a specification that presents unsolved problems as solved is worse than one that has fewer
features.
### 7.1 Replay
**Threat.** A captured Closed Mandate or sealed record is re-presented to obtain a second commitment.
**Mitigation — considered adequate.** Four independent binds: `nonce` unique per `crossing_id` (§5.4 check
8); `exp` evaluated at verification time, not issuance (F1); `agreement_hash` binding the mandate to one
specific Terms Document; and `aud` naming the intended counterparty. A replayed mandate therefore fails on
audience, freshness, and terms simultaneously. `crossing:seal` idempotency (§5.6 req 3) ensures that even a
successful replay of the *sealing* call returns the existing seal rather than minting a second.
### 7.2 Mandate theft
**Threat.** An attacker obtains a mandate and uses it to commit.
**Partially mitigated.** An **Open Mandate is holder-of-key, not bearer**: `cnf` binds it to the authorized
agent's public key, so an attacker holding the mandate but not the private key cannot issue a valid Closed
Mandate under it. This is a material improvement on what the estate has today, where authority is carried by
**bearer secrets in both populations** — an admin session on one side and an API key on the other — neither of which distinguishes possession from authority.
**Residual — a stolen Open Mandate still leaks.** It discloses `magnitude_limit`, `commitment_class` and
`permitted_counterparties`. That is a negotiating disadvantage, not an authority compromise, and it is why
§5.2 restricts disclosure at exchange time.
**🔴 OPEN PROBLEM — revocation.** SD-JWT carries no revocation mechanism. Today, a compromised agent key can
only be neutralised by waiting for `exp`. **Proposed direction**: a revocation projection at the
Verification Authority, keyed by `open_mandate_hash`, that a verifier **MUST** consult during P3 when
online — making revocation a currency check of exactly the kind §6.3 already says cannot be done offline.
This is not specified in this draft and **MUST** be resolved before v1.0. Until it is, `exp` **SHOULD** be
set to the shortest interval the business process tolerates.
### 7.3 Seal forgery
**Threat.** A fabricated seal is presented as genuine, or a genuine seal is altered.
**Mitigated for fabrication and alteration.** A seal is SHA-256 over a canonical serialization of the
Bilateral Record; a verifier holding the record recomputes it (§6.3). Chain insertion is constrained by
`UNIQUE(clearance_id, chain_seq)` and by each link committing to its predecessor's hash, so retroactive insertion breaks every subsequent
link and is detectable by a chain walk.
**🔴 OPEN PROBLEM — the Verification Authority is a single point of trust.** A compromised VA can assert
that a seal resolves when it does not, or fail to resolve one that does. Three partial mitigations exist
and none is complete: the seal is **independently recomputable** by any party holding the record; a full
chain **export** endpoint exists so a party can retain its own copy; and the chain's internal
consistency is checkable without the VA's assent once exported. What none of these gives is *detection by a
third party who holds neither the record nor an export.* Named, not solved.The eventual answer is
probably periodic publication of chain head hashes to an independent witness; that is out of scope for v1.
### 7.4 Downgrade to unsealed commitments
**Full disclosure first.**
A2A extensions **default to inactive**. Activation is requested per-request via the `A2A-Extensions` header,
and the responding agent echoes back what it activated. `required: true` on an Agent Card means the agent
**SHOULD** — not **MUST** — reject a client that has not activated a required extension.
It follows plainly, and this specification states it without softening: **omitting the header is an
available path, by design of the transport.** The A2A extension mechanism does not, and does not attempt
to, guarantee that a governance extension cannot be skipped. **The anti-downgrade property cannot be
inherited from the transport layer.**
**The conclusion this specification draws.**
This is the cleanest available proof of the argument the design analysis and arXiv 2606.31498 make independently:
**governance cannot live at the transport layer.** A transport's job is to move messages between
consenting parties, and a mechanism whose activation is negotiated per-request is exactly right for
capability discovery and exactly wrong for obligation. An obligation that either party may decline to
activate is not an obligation. **Endpoint enforcement is therefore required — not as a workaround for a
gap in A2A, but because it is the only layer at which the property can exist at all.** This is the
"missing architectural layer above current interoperability standards, not a missing feature within them", observed concretely rather than argued abstractly.
**Mitigation — the normative pair (§3.5.1), restated because this is where it earns its place:**
> **OUTBOUND.** A Class 2 implementation **MUST NOT** issue a commitment without a sealed Governed
> Crossing.
> **INBOUND.** A Class 2 implementation **MUST reject** any inbound commitment that does not carry a valid
> Crossing Seal.
**No seal, no deal.** The pair converts downgrade from a silent path into a **detectable violation**: a
downgrading party is refused, the refusal is recorded and auditable (§3.5.2 req 4), and the record of
refusals is itself evidence. The protocol cannot stop a peer from trying; it guarantees that trying fails
visibly and leaves a trace.
**Residual, stated honestly.** Two Class 2 implementations that *both* choose to ignore the pair can
transact ungoverned. No specification can prevent that, and none should claim to. What the specification
gives is that neither can later claim the commitment was governed — because no seal exists, and a seal is
the only thing that proves it.
### 7.5 Sycophancy and pressure negotiation
**Threat**. A counterparty — human or agent — pressures an agent into a commitment its
Principal would not sanction: flattery, urgency, incremental concession, appeals to a relationship, or
simply persistence.
**Mitigated for out-of-bounds commitments — and this is the strongest argument for the Mandate primitive.**
> **The Mandate converts a persuasion problem into a bounded, attributable one.**
An agent talked into exceeding its `magnitude_limit` **cannot close a mandate**. The refusal is F2, and F2
is **structural, not behavioural**: there is no override and no discretion to erode. Persuasion cannot
reach past a constraint the Principal signed, because the agent has no key that produces a valid Closed
Mandate outside it. This is a categorically stronger guarantee than instructing a model to be firm, and it
does not degrade with a longer conversation, a more sympathetic counterparty, or a better-crafted appeal.
Attribution follows: every commitment carries `signer_role` (§4.1.3), so *"an agent committed this under
standing authority"* and *"a human committed this"* are distinguishable at verification time, permanently.
**🔴 OPEN PROBLEM — bad deals within limits.** An agent persuaded into a commitment that is *within* its
magnitude limit but *against its Principal's interest* is **not prevented by this specification, and cannot
be.** The commitment is authorized; it is merely unwise. That is a governance-pack and evaluation-harness
problem, not a
protocol problem.
Naming this boundary precisely is itself a requirement: **this specification bounds authority, not
judgement.** An implementer who believes a Crossing Seal proves a deal was *wise* has misread it. It proves
the deal was *authorized* — which is what this specification's one-line claim says, and no more.
### 7.6 Enumeration at the Verification Authority
**Threat.** The public verification endpoint accepts arbitrary input and distinguishes hit from miss,
performing no prefix validation. That is an unauthenticated
oracle for the existence of identifiers.
**Partially mitigated.** Seal identifiers carry a CSPRNG suffix — `crypto.randomBytes`, 48 bits, adopted
specifically because `Math.random` state is recoverable from observed outputs and a shared PRNG stream
would let one tenant predict another's identifiers. Guessing is
therefore infeasible; the residual risk is confirmation of identifiers obtained elsewhere, and volumetric
probing.
**Open, and already owned.** Logged as a security backlog item in the design analysis, with the timing
deliberately chosen: closing it before the first pilot would cost that pilot its cheapness, and by launch
`ASELF-` will be a registered prefix in the passport namespace, at which point an allowlist
becomes cheap exactly when it becomes necessary. This specification adds one requirement: **a Verification
Authority SHOULD rate-limit unauthenticated resolution**, and **MUST NOT** return differential error detail
that distinguishes "malformed" from "not found".
### 7.7 Open problems — consolidated
| # | Open problem | Section | Required before |
|---|---|---|---|
| **OP-1** | Mandate revocation — SD-JWT has none | §7.2 | **spec v1.0** |
| **OP-2** | Verification Authority is a single point of trust | §7.3 | post-v1.0 |
| **OP-3** | Bad deals within magnitude limits — out of protocol scope | §7.5 | ANAX evaluation harness (out of protocol scope) |
| **OP-4** | Enumeration surface at the VA | §7.6 | Front Office launch |
| **OP-5** | Delivery window between sealing and receipt | §5.6 | mitigated definitionally; no further work required |
---
## 8. Relationship to Adjacent Specifications
Governed Crossing is designed to **compose**. It replaces nothing in the stack.
| Specification | Relationship | Detail |
|---|---|---|
| **A2A core** | **Extends** | Governed Crossing is an A2A extension (§3). It adds no core fields, adds no enum values, and carries all data in `metadata` maps — the three constraints A2A places on extensions. An unextended A2A agent interoperates normally and simply cannot make governed commitments. |
| **A2A Secure Passport** | **Composes — does not compete** | Different objects with different truth-conditions: Secure Passport carries **what the caller asserts about itself** (preferences, session context) for personalisation; a Crossing Seal carries **what a third party verified about a commitment**. A Secure Passport **MAY** carry a reference to a Crossing Seal as one of its shared context items. **The composition is legible precisely because the names differ** (§2.3). |
| **AP2** | **Composes — adjacent domain** | AP2 governs **payment**; Governed Crossing governs **agreement**. Governed Crossing **reuses AP2's open/closed mandate pattern and SD-JWT basis** (§4.1) rather than inventing a credential format. The boundary is exact: AP2's `checkout_hash` binds to a **cart** — a thing with a price. There is no hash target for a **term**. An agreement **MAY** reference a payment made under AP2; the payment remains AP2's. |
| **A2A Timestamp extension** | **Composes** | Both place data in `Message.metadata` / `Artifact.metadata`, namespaced by extension URI, so they coexist without collision. Governed Crossing's own `sealed_at` is authoritative for the seal; Timestamp's values are transport-level and **MUST NOT** be substituted for it. |
| **A2A Traceability extension** | **Composes — complementary** | Traceability records **how** a result was reached; Governed Crossing records **under what authority** a commitment was made. Neither substitutes for the other, and an implementation **MAY** activate both.They are the natural pair for an audit: trace plus mandate answers "what happened and who was entitled to do it". |
| **A2A Agent Gateway Protocol** | **Orthogonal** | Concerns routing and intermediation. A gateway **MUST** forward the `A2A-Extensions` header unmodified; a gateway that strips it induces the downgrade of §7.4 and **MUST** be treated as an untrusted intermediary. |
| **MCP** | **Orthogonal** | Agent ↔ tools. No interaction. |
| **UCP** | **Composes** | Commerce primitives (catalog, cart, checkout). An agreement **MAY** reference UCP artifacts within its Terms Document. |
**Positioning, stated once.** ANAX does not compete with the Linux Foundation ecosystem — it plugs into it,
as AP2 did for payments. That is the whole design intent of expressing this as an extension rather than as a
protocol.
---
## 9. Appendix — Worked Example
**Illustrative only. All values are fictional. IMPLEMENTATION PENDING throughout — no capability shown
below exists today.**
**Scenario.** ANAX's front-office agent issues an **engagement code** to a prospective client — the
accepted first-pilot scenario, here extended to its bilateral form.
> **Note on scope.** The **accepted spike is unilateral**: ANAX seals its own Decision Passport for code
> issuance, `ASELF-` prefix, ~1–2 days, no counterparty involved. This appendix shows what the **same
> commitment** looks like once the Bilateral Crossing exists. The two halves are shown together deliberately, because
> the example also demonstrates the §2.3 naming split in practice: **the Crossing Seal is the wire object;
> the Decision Passport is the ANAX-side artifact.** One commitment, two artifacts, two names, doing two
> different jobs.
**Parties.** *Initiator:* ANAX front-office agent, Principal `urn:anax:principal:anax-standards`.
*Responder:* a prospective client's procurement agent, Principal `urn:anax:principal:example-trust`.
**Commitment act:** `engagement_code_issuance`. **Commitment class:** `irreversible` (§4.1.3).
**Tier:** T1 — agent-signed under standing authority.
### P0 — Discovery and activation
```jsonc
// ANAX agent card, capabilities.extensions[0]
{
"uri": "https://anaxstandard.ai/ext/governed-crossing/v1",
"description": "Bilateral commitments under verified mandates, sealed and publicly verifiable.",
"required": true,
"params": {
"conformance": "full",
"verification_authority": "https://verify.anaxstandard.ai/",
"principal": {
"id": "urn:anax:principal:anax-standards",
"clearance_id": "<withheld>", "trust_basis": "vicca_authority_sealed", "agc_level": 3
},
"mandate_formats": ["sd-jwt"],
"commitment_acts": ["engagement_code_issuance", "proposal_within_tariff"],
"commitment_classes": ["irreversible"] // the ladder value; acts are the separate axis
}
}
POST /message:send
A2A-Extensions: https://anaxstandard.ai/ext/governed-crossing/v1
// ANAX Open Mandate (SD-JWT payload, abbreviated)
{
"vct": "mandate.agreement.open.1",
"iss": "urn:anax:principal:anax-standards",
"sub": "urn:anax:agent:front-office-01",
"aud": "*",
"cnf": { "jwk": { "kty": "EC", "crv": "P-256", "x": "…", "y": "…" } },
"iat": 1786000000, "exp": 1788592000,
"commitment_act": ["engagement_code_issuance", "proposal_within_tariff"],
"commitment_class": "irreversible", // highest class authorised (L-1)
"magnitude_limit": 1, // count of actions (irreversible unit, §4.1.3)
// duration is `exp` above — the code's six-month validity window; scope (one party,
// non-renewable) is a term of the agreement and lives in the Terms Document (§4.2).
"permitted_counterparties": "*"
}
Both sides run the seven screening checks of §5.2. Both pass. State → mandates_pending
(core TASK_STATE_AUTH_REQUIRED).
agreement_hashTerms Document (canonicalized per §4.2): cohort <cohort-id>, one engagement code, six-month validity, no fee,
assessment under <pack> <version>.
Both parties compute independently and compare:
agreement_hash = 9f2c…a1 (ANAX) agreement_hash = 9f2c…a1 (client) ✓ match
State → terms_proposed (core TASK_STATE_INPUT_REQUIRED).
// ANAX Closed Mandate (abbreviated)
{
"vct": "mandate.agreement.closed.1",
"iss": "urn:anax:agent:front-office-01",
"aud": "urn:anax:principal:example-trust",
"signer_role": "agent", // T1 — no human signature
"iat": 1786000420, "exp": 1786004020, "nonce": "b7d1…",
"open_mandate_hash": "4a10…",
"agreement_hash": "9f2c…a1",
"commitment_act": "engagement_code_issuance",
"commitment_class": "irreversible",
"magnitude_asserted": 1, // one action, irreversible unit (§4.1.3)
"governance_basis": { "pack_id": "<pack>", "pack_version": "0.1.0" },
"crossing_id": "d41e…"
}
Each party runs the eight binding checks of §5.4 against the counterparty's Open Mandate. All eight pass —
note magnitude_asserted satisfies magnitude_limit. State → sealing
(core TASK_STATE_WORKING).
Bilateral Record built per §4.4 — mandates by hash, clearance_id by hash. Both parties compute
the joint_seal; values match. Under dual anchoring (§4.4.1) each party then writes its own anchor
into its own clearance-envelope chain, both referencing the shared joint_seal:
joint_seal = c8e4… (identical for both parties — this IS the commitment)
anchor_anax = H(c8e4… ‖ 77b0… ‖ 128) → anax-standards chain, seq 128
anchor_trust = H(c8e4… ‖ 2d51… ‖ 17) → example-trust chain, seq 17
Each party's own chain export now contains this commitment — which is the property Option A was chosen for.
Artifact.metadata["https://anaxstandard.ai/ext/governed-crossing/v1"] = {
"crossing_state": "committed",
"seal_id": "canal-<ts>-<rand>",
"seal": "c8e4…",
"prev_hash": "77b0…", "chain_seq": 128, "is_genesis": false, "seal_version": 3,
"pack_id": "<pack>", "pack_version": "0.1.0",
"outcome": "CLEARED",
"sealed_at": "2026-08-09T21:15:00Z",
"verify_url": "https://verify.anaxstandard.ai/?id=canal-<ts>-<rand>"
}
Core TaskState is TASK_STATE_COMPLETED. ⚖ A verifier MUST NOT read that as commitment (§4.5) —
commitment is the resolvable seal.
Separately, ANAX writes its own Decision Passport for the same action:
<passport-id> → https://verify.anaxstandard.ai/?id=<passport-id>
Deterministic in the submission, frozen namespace, idempotent on retry — the passport-id derivation discipline.
Two artifacts, one commitment. The Crossing Seal is what the counterparty holds and what a regulator resolves to see that a bilateral commitment was made under verified mandates. The Decision Passport is what ANAX publishes about its own governance of the action. Neither is called the other, at any layer — which is §2.3 working exactly as intended.
GET https://verify.anaxstandard.ai/?id=canal-<ts>-<rand>
→ resolves; chain position confirmed; clearance_id absent from the projection (§6.2)
Carried into the specification at v0.4.3 from the licence addendum of 2026-08-25, which the v0.3 merge did not include. Nothing in this section changes a requirement, a wire format or an error name; it states what the text grants, what the names are, and what the operator's services are not.
This specification, its conformance vectors, and the companion specifications published by ANAX Standard
(Mandate Object Specification, Crossing Taxonomy, Sovereign Channel Message Contract — forthcoming) are licensed under
Creative Commons Attribution 4.0 International (CC BY 4.0). SPDX: CC-BY-4.0. Attribution:
"ANAX Standard — anaxstandard.ai". No licence is required to implement this specification.
Reference implementations published by ANAX Standard are licensed under Apache License 2.0.
SPDX: Apache-2.0.
Marks. Governed Crossing™, Crossing Seal™ and Decision Passport™ are trademarks of ANAX Standard. Governed Crossing™ designates the protocol and may be used by any conformant Implementer to describe conformance. The names ANAX, ANAX Standard, ANAX Institute, VICCA, VICCA ONE, FRYKTORIA, ANAX Canal and X-Layer are trademarks of their owner and are not licensed by this document.
Services. Operation of a Registration Authority, Verification Authority, or Centre by ANAX Standard is a service and is not licensed by this document.
Everything this draft knowingly leaves open. Nothing here is a discovery for a later reader to make.
| # | Item | Source |
|---|---|---|
| ✅ DONE (2026-08-09) — the extension URI serves the specification. Live at https://anaxstandard.ai/ext/governed-crossing/v1 (rendered) and https://anaxstandard.ai/ext/governed-crossing/v1.md (raw markdown). A sanitized publication copy is served; this file remains canonical. An implementer encountering the extension URI can now resolve it and read the spec, as A2A's discovery model assumes. | — | |
✅ CLOSED (2026-09-01) — confirmed against the A2A protocol buffers definition: AgentExtension.params is google.protobuf.Struct params = 4; Message.extensions is repeated string extensions = 7; Artifact.extensions is repeated string extensions = 6. PENDING CONFIRMATION markers removed from §3.3 and §4.3.1/§4.3.2. |
— | |
| ✅ CLOSED — chain placement decided (Option A, dual anchoring) and §4.4 requirement 4 restated for the joint-seal / anchor split. Retained struck-through as record; not one of the outstanding items. | — | |
| ✅ CLOSED (2026-08-31) — RFC 8785 (JCS) adopted; the adoption gate passed with TD-1…TD-5 and two independent implementations producing byte-identical digests. §4.2, §5.8. | — | |
| 11-5 | Specify mandate revocation (OP-1). SD-JWT has none; today a compromised agent key can only be neutralised by waiting for exp. |
§7.2 |
| ✅ CLOSED (2026-09-01) — §3.7 gives params, result, a 17-name error vocabulary with JSON-RPC and HTTP codes, determinism and versioning for both methods; §3.6 gives the per-transport binding. | §3.6, §3.7 | |
✅ CLOSED (2026-09-01) — commitment_class fixed to the five-value ordered ladder with L-1…L-5; magnitude_limit fixed as a non-negative integer whose unit is set by class, cumulative per Mandate, with M-1…M-5; commitment_act added as the separate axis. |
§4.1.3 | |
| 11-16 🔴 | ⚠ v0.4 note, 2026-09-09 — what is enforced, and what is not. The 2026-09-01 wording below described a specification with no implementation and is retained as the record of that date; it is now wrong in both directions, and the direction that matters is that it understates what runs. Measured, and dated: crossing:seal and crossing:verify are implemented and conformant — the responder implementation and the Verification Authority's resolver; the first bilateral crossing was sealed and publicly verified on 2026-09-07 (joint_seal computed identically by both parties; genesis anchor written), with WC-2, WC-3, WC-5, WC-6 exercised live and WC-1, WC-4, WC-8, WC-8b, WC-9 green in the implementation's own suites. commitment_class IS enforced on the crossing path by the authority judge (L-1) and, for a bilateral crossing, by the full §5.4 check set including check 9 (party binding). magnitude_limit IS enforced atomically at the Registration Authority: a Closed Mandate is spent once, whole (M-4b), and its draw is booked against its Open at issuance (M-4a). CM-1…CM-6: 4 pass, 2 conditional, 0 fail — CM-4 and CM-6 are conditional because financial is unreachable under the operator's charter, and their reachable half is proven at issuance. STILL NOT ENFORCED — this item stays 🔴 until each of the three closes: (i) the mandated-act list is three acts (crossing:seal for both parties; one chain-verification read; one credential-provisioning act) and exists only in prose — there is no committed list and no parity check between the list and the code, so a gate can be removed or acquired without anything noticing. Canal's main path is Mandate-optional by charter, not by omission: the operator's charter names credential issuance as the one provisioned act. (ii) a commitment_class is a caller's declaration with no floor derived from the act, so the ladder is enforced against a self-declared number — the first live bilateral crossing declared observe for crossing_seal, an act that wrote an immutable anchor and spent two Mandates, and every check passed because every check compared the declaration to the declarer's own Mandate. (iii) the canonical WC suite has no live mode and therefore certifies nothing that runs: it now locates the implementation and still reports every vector as NOT IMPLEMENTED. ⚖ This specification MUST NOT be read as describing a fully enforced system. It MAY now be read as describing a system whose bilateral crossing path is implemented, conformant and live, and whose single-party path is governed only where the Charter says it is. Overclaiming the first half and underclaiming it are both refused. — The 2026-09-01 record follows. ⚠ ENFORCEMENT — the vocabulary and the wire contract are specified and NOTHING IMPLEMENTS OR ENFORCES EITHER. §4.1.3's rules have no enforcing component: the only envelope validator in the reference implementation reads jurisdiction, sector, decision type, automation level, special category, children's data and oversight, and contains no reference to commitment_class or magnitude_limit. §3.7's methods have no handler, route or method string anywhere. Measured 2026-09-01: CM-1…CM-6 fail 6 of 6; WC-1…WC-7 fail 7 of 7, the CM vectors by being silently accepted and the WC vectors by having nothing to call. This specification MUST NOT be read as describing an implemented system, and v0.3 MUST NOT claim enforcement or wire conformance, until both vector suites pass. Publishing a vocabulary as though it were a control is the failure this project has already paid for twice. |
§3.7, §4.1.3 |
| 11-17 🟡 | The draw-at-issuance rule's two known costs, stated so nobody discovers them live (v0.4, 2026-09-06). (a) No release at v1. A Closed Mandate that expires unsealed leaves its draw booked against its Open (M-4a); nothing gives it back. The Open's own exp bounds the loss; a release primitive (or reserve-then-confirm) is surface the Registration Authority does not have and is not specified here. (b) A chain-write failure after the spend needs a fresh Closed. M-4b spends both Closed Mandates before the anchor write (a refused crossing is never anchored — the invariant); if the write then fails, §5.5 req 4 applies (failed_unsealed, retry permitted) but the spent Closed cannot be reused — the retry draws a new Closed from the Open. This is the cheap failure, accepted; the expensive one — an anchored crossing with no authority behind it — is impossible by construction. Closes when a release or two-phase spend is specified and implemented, or when v1.0 accepts both costs as final. |
§4.1.3 M-4a/M-4b, §5.5 req 4 |
| # | Item | Source |
|---|---|---|
| 11-8 | Verification Authority as a single point of trust (OP-2). Probable direction: periodic publication of chain head hashes to an independent witness. | §7.3 |
| 11-9 | Multilateral crossings. v1 fixes parties at exactly two. More than two is out of scope, deliberately, and is an omission with a stated reason rather than an oversight. |
§4.4 |
| 11-10 | Enumeration surface at the Verification Authority (OP-4). Already owned in the internal security backlog, with timing deliberately chosen: close before Front Office launch, when an allowlist becomes cheap exactly as it becomes necessary. | §7.6 |
| 11-14 | Multilateral Crossing — N-party joint commitments under a single joint_seal. The dual-anchoring model generalizes: joint_seal remains party-independent; anchor_i per party, N anchors. Natural fit with the central crossing registry (the Option D path at §4.4.1). Candidate for /v2 or a companion extension. (Expands 11-9, which records the v1 scope decision; this is the design sketch, that is the deferral.) |
§4.4, §4.4.1 |
| 11-15 | Collective Mandate — mandates issued by group principals without a single representative: threshold/multisig issuance (k-of-n internal signatures) so the Verification Authority can verify quorum was met WITHOUT visibility into internal deliberation. Bridges this specification's authority layer to community-governance dimensions (deliberation, voting) that remain out of scope per §7.5. | §4.1, §7.5 |
| # | Item | Owner |
|---|---|---|
| 11-11 | The Mandate primitive — object, store, issuance, and the organizational delegation chain. This specification consumes it and MUST NOT fork it. | ANAX platform — the Mandate primitive |
| 11-12 | The Bilateral Crossing — the second party in the Canal contract and seal basis, with a sequencing rule that keeps new crossings from appearing superseded. | ANAX platform — the Bilateral Crossing |
| 11-13 | Bad deals within magnitude limits (OP-3). Out of protocol scope by construction — this specification bounds authority, not judgement. Belongs to the evaluation harness, which does not exist in any repository today. | ANAX evaluation harness (out of protocol scope) |
Normative, additive (E-3): no name removed or renamed; MANDATE_PARTY_MISMATCH and MALFORMED_MANDATE
each gain a case. §5.4 check 9 (party binding) now holds seven equalities: the seventh binds each party's
Closed Mandate to the record's act — closed.commitment_act == record.commitment_act. Six equalities bound
bytes, subjects and the Open and left the act loose, so a Closed drawn for one act could seal a record
declaring another with every check green. Stated with it: a Closed Mandate MUST name the act it is drawn
for; the responder MUST refuse, as MANDATE_PARTY_MISMATCH, a Closed whose act is absent or differs
from the record's, and, as MALFORMED_MANDATE, a record whose act is outside the profile's act vocabulary
(§4.1.3) — an unlisted act has no floor, and "no floor" may not mean "no floor check". §4.1.3's note that
L-5 does not make an unrecognised act malformed stands: the act is bounded by the vocabulary at the seal, not
by the class ladder. The v0.4 bullet below is corrected to the same count. Reference implementation: the
seal binds the act to the record (the seventh equality of check 9) and refuses an unlisted act; the Registration
Authority refuses to issue an act-less Closed; the row holds the same rule as a CHECK.
Additive; no wire, rule or error-name change. New §9A Licence, Marks and Services carries the licence addendum of 2026-08-25 that the v0.3 merge did not include: the CC BY 4.0 / Apache-2.0 split (specification text and vectors / reference implementations), the trademark notice (Governed Crossing™, Crossing Seal™, Decision Passport™; the ANAX product names reserved), the statement that Governed Crossing™ may be used by any conformant Implementer to describe conformance, and the clause that operating a Registration Authority, Verification Authority or Centre by ANAX Standard is a service not licensed by this document. ™ added to the §2 definitions of Governed Crossing and Crossing Seal. §4.4: the anchor conformance vectors AN-1 and AN-2 are now published in the specification itself, with the two fixture preimages, instead of being cited from an addendum the public copy did not carry.
MAGNITUDE_MISMATCH, magnitude equality, WC-11 (normative)Normative addition. Added the error name MAGNITUDE_MISMATCH −40035 (HTTP 403, raised by seal) to
§3.7.4, a magnitude equality rule in §5.4 (the record's magnitude_asserted MUST equal the asserted
magnitude of each party's Closed Mandate; a mismatch is refused before any consumption), and conformance
vector WC-11. This is an integrity check distinct from the budget ceiling: MAGNITUDE_EXHAUSTED means
the amount exceeds the Mandate; MAGNITUDE_MISMATCH means two sealed documents disagree on the amount.
Additive only — no name removed or renamed (E-3). Consistency edit in the same area: §5.4 check 5, the
F2 failure row and the P3 walkthrough said magnitude_asserted satisfies "every dimension" of
magnitude_limit; §4.1.2 has always defined it as a single integer in the unit the class fixes, and no
Mandate in this specification carries a per-dimension object. The three passages now say "the magnitude";
the integer definition stands. The withdrawn wording is noted in place.
MANDATE_REVOKED and WC-10 (normative)Normative addition. Added the error name MANDATE_REVOKED −40014 (HTTP 401, raised by seal) to
§3.7.4, a revocation-at-commit rule in §5.4 (the responder re-reads both Closed Mandates' status inside
the seal-writing transaction under a read lock and refuses if either is no longer active; the re-read is a
read, consumption stays with the Registration Authority), and conformance vector WC-10 (valid at
consumption, revoked before commit → MANDATE_REVOKED, nothing sealed, refusal recorded with both
consumptions spent). Closes the window between consumption and commit. Additive only — no name removed or
renamed (E-3). The wire form of the Mandate Object Specification (forthcoming) §2.4 for the bilateral path.
Two cross-references corrected before publication: the spent-cost cite resolves to §11-17 cost (b), and the
Mandate Object Specification is cited as forthcoming, as the Authority Doctrine is.
This is a normative revision, not an editorial one. Between the v0.3 publication (2026-09-01) and this version the following were added to the specification and are carried into the published copy for the first time:
ROLE_MISMATCH −40041,
SERVICE_HELD_MANDATE −40042 and MANDATE_PARTY_MISMATCH −40043 are added to the error table.agent_id MUST equal its Closed
Mandate's subject and the two open_mandate_hash values MUST agree; six equalities, all MUST hold
(seven from v0.4.4: the act, see that entry).anchor_i = SHA-256(UTF-8(joint_seal | prev_hash ?? "genesis" | chain_seq)),
with published vectors AN-1/AN-2.crossing:verify audience — an Open's aud is the Verification Authority (urn:anax:va:001) as the
Registration Authority issues it; an Open's counterparty binding is permitted_counterparties, never
aud; aud = counterparty is the Closed's (§5.2 item 5, §5.4 check 7).commitment_class and magnitude_limit are enforced on that path; the item stays
open for the three named reasons. §11-17 added.Alongside those additions, the published copy was audited against the publication filter (Standard Doctrine §4ter) and the passages that carried operational detail were rewritten: a customer example (cohort and engagement) and live-issued identifiers (a passport id, seal ids) are replaced by placeholders; internal function, constant, endpoint and repository names are replaced by their role in prose; internal decision labels and test labels are replaced by the rule they name; product id-namespace prefixes are generalized; the certificate-registry host and the portal lookup-logic sentence are removed; the Authority Doctrine is cited by name as forthcoming. A licence line (CC BY 4.0) is added to the front matter. The publication projection enforces the same filter and refuses to write a public copy that carries any of these terms.
Four addenda, drafted separately and merged together in one step. Merging them one at a time would have left
this document disagreeing with itself in the interval — §4.1.3 carrying an integer magnitude_limit while
§4.4 still carried the object, or §3.6 naming a transport binding for methods whose error vocabulary had not
landed.
agreement_hash fixed to SHA-256(JCS(terms_document)) under RFC 8785;
Terms Document constraints R-1…R-5; conformance vectors TD-1…TD-5. the adoption gate passed (§5.8).
Closes 11-4.commitment_class fixed to the five-value ordered ladder
(observe < reversible < irreversible < financial < binding) with L-1…L-5; magnitude_limit
fixed as a non-negative integer whose unit is set by class, cumulative over the Mandate's lifetime, with
M-1…M-5 and currency required for financial; commitment_act added as the separate axis of
what act is committed. Conformance vectors CM-1…CM-6. Closes 11-7.crossing:seal and crossing:verify, a
17-name error vocabulary with JSON-RPC and HTTP codes, determinism, versioning, and vectors
WC-1…WC-7. §3.6 gains the per-transport binding table.
Closes 11-6.AgentExtension.params, Message.extensions and Artifact.extensions
confirmed against the A2A protocol buffers definition; PENDING CONFIRMATION markers removed from §3.3, §4.3.1 and
§4.3.2. Both schema questions move to CONFIRMED. Closes 11-2.engagement_code_issuance and
proposal_within_tariff are commitment_act values with commitment_class: irreversible beside them,
and its magnitude_limit object becomes an integer count.Why v0.3 publishes at the /v1 extension URI, and why that satisfies §3.1. The URI identifies the
extension, not the document; §3.1 freezes it and requires a version increment only for an incompatible
change, with /v1 remaining resolvable. Everything in v0.3 is additive at the extension level: a new
claim (commitment_act), a wire contract for methods §3.6 already named, new error names, and a
canonicalisation that replaces a placeholder. §3.7.6 states the same rule for method evolution.
The one genuine break is named rather than glossed: magnitude_limit changes from an object to an
integer. That is incompatible in principle — but the field it changes carried IMPLEMENTATION PENDING and
was marked a proposal in v0.1, no implementation of it exists anywhere, and no counterparty has ever
activated this extension, so there is no deployed reader whose parse it can break. A /v2 here would
freeze a placeholder into the URI space and buy nothing. Recorded so that a later reader can judge the
call rather than discover it.
⚠ What this release does not do. It specifies. Nothing in it is implemented or enforced: CM-1…CM-6 fail 6 of 6 and WC-1…WC-7 fail 7 of 7 as of 2026-09-01. See §11-16.
Nights 1–2. Nine design deviations raised and resolved during drafting (internal register). Published at the extension URI.
This draft is published for community review. Comment, objection, and implementation experience are all welcome — in particular where a normative requirement is unimplementable, ambiguous, or wrong.
Governed Crossing v0.4.4 DRAFT · ANAX Standard — ANAX Institute · 2026-09-17
Extension URI: https://anaxstandard.ai/ext/governed-crossing/v1 · Verification Authority: verify.anaxstandard.ai