Normative v1.0-Draft · March 2026 EP 25 000 039.5 · EP25209644

AGTS Clearinghouse — Normative Specification

The client-side implementation that runs the governance measurement loop, operates the Policy Validator network, assembles Governance Envelopes, and submits canonical leaves to the Transparency Log.

Supersedes: rtr-clearinghouse/ (pre-AGTS; retained as historical reference) · Normative refs: AGTS-TERMS · AGTS_CRYPTO_SPEC · PROTOCOL_DIAGRAMS

This document specifies the AGTS-conforming clearinghouse: the client-side implementation that runs the governance measurement loop, operates the Policy Validator network, assembles Governance Envelopes, and submits canonical leaves to the Transparency Log. Every protocol behavior defined here derives directly from AGTS-TERMS.

§1 — Scope

Scope

The AGTS Clearinghouse is the governance client layer. It sits between the Machine Execution Layer and the Transparency Log. Its responsibilities:

01

Accumulate a signed, Merkle-chained measurement chain of governance evidence

02

Operate the Policy Validator network — BFT vote collection, quorum certificate issuance

03

Assemble AGTS Governance Envelopes: Proof Bundle + validator signatures + Sovereign Authority signature

04

Submit completed Governance Envelopes to the Transparency Log for Canonical Leaf admission

05

Integrate with the settlement rail via AGTS settlement receipts

Scope exclusions — normative

The clearinghouse is not a transparency log. It produces artifacts admitted to a log; it does not host the log itself. The clearinghouse is not a Sovereign Authority. It delivers the quorum certificate to the Sovereign Authority for hardware signing; it does not hold or emulate authority keys.

§3 — Identity model

Identity model

§3.1 Node ID derivation

Every participant in the AGTS network is identified by a node_id derived from their public key:

node_id = hex( SHA256( DER_SubjectPublicKeyInfo_bytes ) )

Output: 64 lowercase hex characters. No truncation at any length.

// WebCrypto — correct implementation async function nodeIdFromPublicKey(publicKey) { const spkiBuffer = await crypto.subtle.exportKey('spki', publicKey); const hashBuffer = await crypto.subtle.digest('SHA-256', spkiBuffer); return Array.from(new Uint8Array(hashBuffer)) .map(b => b.toString(16).padStart(2, '0')).join(''); }

The SHA-256 input MUST be the raw DER bytes returned by exportKey('spki', ...). It MUST NOT be a hex-encoded or base64-encoded string of those bytes.

§3.3 Node ID scope

ContextField nameValue
Settlement clientnode_idSHA256(client SPKI)
Policy Validatorvalidator_idSHA256(validator SPKI)
Sovereign Authorityauthority_node_idSHA256(authority SPKI)
Log Operatorlog_idSHA256(log operator SPKI)
Witnesswitness_idSHA256(witness SPKI)
Monitormonitor_idSHA256(monitor SPKI)
§4 — Cryptographic protocol

Cryptographic protocol

§4.1 Signing

step 1: body = { ...artifact_fields } step 2: canonical = canonical_json(body) // RFC 8785 step 3: digest = SHA256( UTF-8(canonical) ) // raw 32-byte digest step 4: signature = Sign(private_key, digest) // Ed25519 or ECDSA P-256 step 5: sig_b64url = base64url(signature_bytes) // no padding

The correct signed message is the raw 32-byte SHA-256 digest of the canonical JSON. Not the hex encoding of that digest. Signing the hex string is a protocol violation.

§4.2 Canonical JSON

canonical_json(x) follows RFC 8785: keys sorted lexicographically by Unicode code point; no insignificant whitespace; UTF-8 encoding, no BOM; IEEE 754 double-precision numbers. Non-finite numbers (Infinity, NaN) are protocol errors — MUST NOT appear in any artifact body.

§4.3 Signature encoding

AlgorithmFormatSizeWire field
Ed25519Raw bytes, base64url (RFC 4648 §5), no padding64 bytessignature_b64url
ECDSA P-256IEEE P1363 (r ‖ s), base64url, no padding64 bytessignature_b64url
§5 — Validator network

Validator network

§5.1 Configuration

ParameterValueConstraint
n_validators4n = 3f + 1 — tolerates f = 1 Byzantine node
quorum_threshold3≥ ⌊2n/3⌋ + 1
proposer_selectionH(action_id) mod n_validatorsDeterministic

§5.4 Vote protocol — AGTS_VOTE_V1

{ "type": "AGTS_VOTE_V1", "validator_id": "<64-char hex node_id>", "decision": "ACCEPT | REJECT", "body_hash": "<64-char hex — SHA256(canonical_json(proof_bundle_body))>", "voted_at": "<ISO-8601 UTC>" }
§6 — Proof bundle

Proof bundle

The Proof Bundle (AGTS_PROOF_BUNDLE_V1) is the governance evidence artifact submitted to validators. It is the primary input to the clearinghouse pipeline.

Core fields
  • type, version
  • node_id, subject_id
  • generated_at
  • parent_bundle_hash
Evidence fields
  • gate_results — G1 through G5
  • evidence — four G4 hashes
  • state_before_hash
  • state_after_hash
  • replay_seed
  • artifact_hash, payload_uri
  • signature_b64url

The parent_bundle_hash field chains this bundle to the immediately preceding bundle in the measurement chain. This creates a Merkle-chained history that is non-repudiable and non-reconstructible after the fact.

§7 — Governance envelope

Governance envelope

The Governance Envelope (AGTS_GOVERNANCE_ENVELOPE_V1) wraps the Proof Bundle with the quorum certificate and Sovereign Authority signature. It is the artifact admitted to the transparency log as a canonical leaf.

proof_bundle

The complete signed Proof Bundle

quorum_cert

Array of ≥ 3 AGTS_VOTE_V1 records with ACCEPT decisions and valid signatures

authority_sig

Sovereign Authority ECDSA P-256 signature over the envelope body — hardware-backed on GrapheneOS Pixel Titan M2

log_binding

{ log_id, submitted_at } — cryptographically binds this envelope to a specific transparency log

An envelope MUST NOT be submitted to the log without a valid authority_signature. A log worker MUST verify the authority_signature before admitting the leaf. Admission without verification is a protocol violation.

§8 — Canonical leaf

Canonical leaf and leaf hash

leaf_hash = SHA256( "AGTS_LEAF_V1" || canonical_json(governance_envelope) )

The prefix "AGTS_LEAF_V1" is domain separation — it ensures that leaf hashes from different protocol versions cannot collide even if the envelope bodies are identical. The leaf hash is the permanent, globally unique identifier for this governance decision.

§9 — Signed tree head

Signed Tree Head (STH)

{ "type": "AGTS_SIGNED_STH_V1", "log_id": "<64-char hex — SHA256(log_operator DER SPKI)>", "tree_size": <integer — number of admitted leaves>, "root_hash": "<64-char hex — Merkle root>", "timestamp": "<ISO-8601 UTC>", "log_signature": "<base64url ECDSA P-256>", "witness_signatures": [ ... ] // L4 only }

log_signature covers {type, log_id, tree_size, root_hash, timestamp} only. The witness_signatures array is excluded from the signed body. Witnesses sign independently against the same five fields.

This page covers the most referenced sections. The complete 759-line specification is in the GitHub repository at agts-clearinghouse/AGTS_CLEARINGHOUSE_SPEC.md.

Full spec on GitHub → Plugin API → Normative vocabulary →