Files
HIPs/HIPs/hip-1163-x402-pay-per-request.md
zeekay ad5a010f79 Three words for what a proposal is, and a generator that refuses
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.
2026-08-28 18:05:41 -07:00

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

Released under CC0 1.0 Universal Public Domain Dedication.