The same fact lived in nine hand-kept places and they disagreed about 58
proposals. This makes the file the only authority and everything else a
projection of it.
ONE VOCABULARY, in vocabulary.json, read by the lint, the generator and the
doc site. Three words, each answering "can I rely on this?" and nothing else:
Draft nothing implements it yet, or its own text says the work is ahead
Final the thing it specifies exists in the code
Living governance and process: amended in place, never finished
Eight spellings folded in. `Active` was the one doing real damage: on four Meta
and Process HIPs it meant living, on 48 others it meant shipped, and one word
doing two jobs is why nothing agreed. `Review` and `Last Call` described a
queue rather than a document, and a proposal under review is still one you
cannot build against.
STATUS IS EARNED, not declared. Every Standards Track proposal was checked
against the estate: 235 have their implementation, and eight do not and say so.
HIP-0095 carries a twelve-week plan ending at "mainnet launch" and has no code.
HIP-0525 specifies an all-machine meeting while apps/meet refuses a machine a
seat. HIP-0078 names a prover at a "planned location" that does not exist.
HIP-0114 is the cog.* procedure set, and that procedure set returns nothing.
HIP-0003, HIP-0102, HIP-0305 and HIP-0901 likewise: looked, found nothing.
SIXTEEN TOMBSTONES DELETED rather than statused. A dead document kept alive in
the index is one concept wearing two names. Each successor exists and carries
the text, and the 90 references that pointed at a tombstone now point at it --
whole markdown links at once, so link text cannot outlive its target. Three of
the sixteen were pure compatibility: HIP-0028 specified a migration off a
Bitnami chart, HIP-0029 dual-engine MySQL, HIP-0030 a schema-deprecation
policy. We ship no backwards compatibility, so we do not specify it.
Nine links were already broken and had been since the corpus was renumbered:
short-form ./hip-5.md paths that never existed. Two named the wrong proposal --
one cited "HIP-1: Hanzo Multimodal Models" where multimodal is HIP-0003.
THE GENERATOR REFUSES more often than it writes, because a short table looks
exactly as authoritative as a complete one. Rows must equal files; a shrinking
index must say by how much; a section whose input is missing is an error, not
an omission; nothing is written until all of it validates. Its self-test breaks
the corpus eleven ways and requires each to refuse AND leave README byte-
identical -- a guard that reports after half-writing has protected nothing.
The lint's second implementation of the same comparison is gone.
The site rendered 52 proposals with a fallback grey badge and counted them
toward none of its three stat tiles, because docs/lib/source.ts declared a
status union by hand: it admitted a `Stagnant` no file has ever carried and
omitted the one 52 files did. Five components each kept their own copy of the
colours. All six now read vocabulary.json.
6.9 KiB
hip, title, author, type, category, capability, status, created, requires
| hip | title | author | type | category | capability | status | created | requires |
|---|---|---|---|---|---|---|---|---|
| 1163 | x402 — Pay Per Request | Hanzo AI | Standards Track | Core | x402 | Final | 2026-08-20 | HIP-0026, HIP-0106, HIP-0139 |
HIP-1163: x402 — Pay Per Request
Abstract
x402 is pay-per-request over HTTP 402: quote a price, take the payment, serve
the resource. It speaks x402 protocol version 2 — challenge, EIP-3009
transferWithAuthorization signed over the advertised terms, verify, settle
exactly once, serve — native to the cloud binary. It is a rail and nothing
else: the marketplace declares what is priced and who is paid, wallets says
where the money lands, and this capability enforces. The implementation is
hanzoai/cloud apps/x402.
Motivation
A marketplace where agents buy tools and data per call needs a payment that works without an account relationship: the buyer may be a client that has never heard of the seller, and the price may be a fraction of a cent. HTTP 402 with a signed transfer authorization is the shape that fits — the challenge carries the terms, the retry carries the proof, and no session, invoice or card vault sits between them. What it demands in exchange is exactness: replay, double-settlement and misdirected credit are all one-request attacks if the rail is loose anywhere.
Specification
The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in RFC 2119.
§1 The wire is the spec's, not ours
The protocol is x402 version 2 as specified by x402-foundation/x402
(specs/x402-specification-v2.md and its HTTP transport binding). Every field
name, header name and error reason on the wire MUST be the one the
specification prints (apps/x402/protocol.go): the wire is a contract with
other people's clients, and a client that has never heard of Hanzo must be
able to pay us. The version is a number on the wire (x402Version: 2) and the
only thing that decides how a message is read — a wire whose shape depends on
an operator flag is two wires. One scheme (exact), one transfer method
(EIP-3009); signature verification is secp256k1 recovery over the EIP-712
domain the challenge itself stated. The default settlement network is
eip155:36963, the Hanzo L1, matching wallets.
§2 The store, and settle-once
One SQLite store, x402 (apps/x402/store.go:55): one row per claimed
authorization, keyed by a deterministic id derived from (payer address,
nonce). That id is both the replay-dedup key — a primary key, so a second
insert of the same authorization is atomically refused — and the idempotency
ref on both money writes, so the fast in-pod dedup and the ledger's own
idempotency agree on one key. A row is written at claim and flipped at settle;
an interrupted settlement is recoverable because the claim names payer, payee,
amount and the payee's ledger subject, and startup Reconcile completes
anything the last process left in doubt (apps/x402/x402.go:181). The payee
subject is on the row and MUST NOT be re-resolved at completion time — a claim
has to be sufficient on its own, or the sweep silently pays whatever the
wallet resolves to later.
§3 The address, and the three callers
The addressed surface is one typed operation, GET /v1/x402/settlements/{id} (manifest/apps.go:174) — the receipt lookup,
scoped by principal.Ledger, which folds in the SuperAdmin masquerade so an
admin inspecting another org still reads their own settlements
(apps/x402/x402.go:791). The flow itself is written once (run) and reached
three ways: Enforce, a handler a priced route group applies; Settle, the
same flow for a resource named in a request body; and the plane op
/x402/settle for the fleet's one-binary-per-app shape, where the payer is
the caller's org resolved at the edge and never a field
(apps/x402/rpc.go). On the plane the outcome is data, not a transport error
— a 402 carries the terms the client must read to pay — so a transport error
means the rail did not answer, and callers fail closed on it, never free.
§4 Money
The surface declares cloud.Free (plugin/x402/main.go): the rail itself
charges nothing per request. The money it moves is the settlement — a payer
debit through the metering spine under provider label x402 with the
settlement id as Ref (apps/x402/x402.go:732), which is how paid usage
appears in billing/usage like any metered spend, and a payee credit deposited
into the ledger subject of the wallet the publisher's listing named, resolved
through wallets (HIP-1161). What a resource costs is the marketplace
registry's table; when the price cannot be learned the answer MUST be an
error, never "free" — a rail that reads its own outage as "nothing is priced"
sells the whole catalogue for nothing (apps/x402/peer.go). Unknown payee is
503; unreachable ledger is no settlement, no receipt, no dispatch.
§5 Events and telemetry
It publishes nothing on the bus; a customer's webhooks receive no x402.*
events. Beyond the request span, each settlement appends an audit record
(resource type x402.settlement, redacted; apps/x402/x402.go:899), and the
startup reconcile logs what it completed.
§6 Upstream
It implements the x402 specification version 2 (x402-foundation/x402) — a
wire specification, not a code fork; no upstream implementation is embedded.
Signature recovery rides github.com/luxfi/crypto v1.20.5 (Lux Ecosystem
License 1.2), the same primitive wallets custody signs with. Persistence is
github.com/hanzoai/sqlite (MIT / Apache-2.0 dual).
§7 Stage
beta: the manifest row declares it (manifest/apps.go:174, Stage: Beta;
HIP-0139 §8), reached by flag until promoted.
Rationale
The alternative to a rail-plus-registry seam is a payments module inside each priced subsystem. That is N copies of verify-and-settle, N nonce stores that do not share a dedup key, and a marketplace that cannot price a resource without shipping code into its owner. One rail, inert until a registry is published, keeps enforcement in one place and pricing where the catalogue is.
Security Considerations
The attacks are the classic payment ones, and each is closed structurally.
Replay: the (payer, nonce) primary key refuses a second claim atomically, and
the same id is the ledger's idempotency key, so even a bug in the fast path
cannot double-move money. Redirection: the payee org comes off the listing
row and the payer off the edge-resolved principal — neither is a request
field, so a buyer can neither choose who is paid nor who pays. Free-fall: the
priced set must fail closed (§4); the one exception this rail ever held —
reading an unreachable marketplace as "no marketplace, nothing priced" — is
recorded in apps/x402/peer.go as the defect that sold everything for
nothing, and is why the price has no exception now.
References
- HIP-0026 — Identity and Access Management
- HIP-0106 — Hanzo Plugin Contract
- HIP-0139 — Capability
- HIP-1161 — Wallets — Key Custody
Copyright
Released under CC0 1.0 Universal Public Domain Dedication.