Skip to content

Architecture: three parties and one HTTP boundary

SubEtha’s runtime is three parties talking over plain HTTP, plus the local zERC20 stack that only one of them ever touches. This page maps the parties, the HTTP surfaces between them, and the trust boundaries — who holds which keys and who can do what. The wire format itself is the wire protocol reference; the lifecycle semantics are x402 v2 on SubEtha and Accepted vs finalized.

payer (AI agent / script)
│ x402 v2 over HTTP: request → 402 offer → paying request → resource
provider (resource server, official x402 middleware + SubEtha scheme plugin)
│ facilitator HTTP API: /supported /challenge /verify /settle
facilitator (settlement daemon)
│ RPC + indexer + prover — the only party that touches the chain
local zERC20 stack (anvil, deployed contracts, indexer, decider) → treasury
  • The payer requests the resource and pays. It sees only what the 402 offer shows it: token, network, quoted amount, expiry, and a one-time burn address as payTo. It is never shown the provider’s real payout destination.
  • The provider serves the HTTP resource behind a paywall. It runs the official x402 middleware with the SubEtha scheme plugin and delegates every payment question to the facilitator.
  • The facilitator derives the one-time burn addresses, verifies and settles payments, and later drives finalization (the proof-gated mint). It is the only component that talks to the chain, the indexer, or the prover.

Everything between parties is HTTP — there is no shared database, queue, or SDK linkage across party lines.

Payer ↔ provider is unmodified x402 v2: the PAYMENT-REQUIRED header on the 402 response (the offer), PAYMENT-SIGNATURE on the paying request (the payment), and PAYMENT-RESPONSE on the served response (the settlement result). SubEtha adds no headers and provides no x402 v1 fallback.

Provider ↔ facilitator is the standard x402 facilitator API plus one SubEtha extension:

endpoint why the provider calls it
GET /supported at initialization — the middleware hard-fails unless the facilitator advertises the subetha-zerc20 kind
POST /challenge per unpaid 402 — a burn address can only be derived by the facilitator, so a provider cannot mint its own offer the way vanilla x402 sellers do
POST /verify on a paying request — a read-only, point-in-time check
POST /settle after the resource handler ran — executes the burn-confirmation path; success means accepted

The facilitator treats the requirements echoed in these calls as untrusted and compares them against its own challenge record, rejecting any mismatch.

party holds can
payer its own EOA key sign or broadcast a burn to the offered payTo; nothing else
provider no keys in the shipped configuration — not even a payout EOA’s serve or withhold the resource; it never touches the chain or the zERC20 SDK
facilitator SUBETHA_TREASURY_PK, SUBETHA_RELAYER_PK, the burn-address derivation seed, optional SUBETHA_ADMIN_TOKEN derive offers, broadcast permit burns, submit proof-gated mints, sweep fees

Three consequences of that split:

  • The payout destination lives facilitator-side. The provider’s route config carries no payout address or secret; the treasury (or the FeeSplit contract) is named in the facilitator’s configuration. A compromised provider process therefore leaks no settlement keys.
  • The BUSL boundary is confined to the facilitator. All use of @zerc20/sdk (BUSL-1.1) and its Node/wasm specifics lives inside the facilitator’s settlement backend. Payer and provider integrate through Apache-2.0 packages and plain HTTP.
  • The facilitator is fully trusted for settlement. It sees every payment, derives every burn address, and controls when finalization runs. What that trust means in practice — and what failure looks like at each boundary — is Trust & Limits.

The facilitator settles against the official zERC20 stack running locally: anvil as the chain, the deployed official contracts, the indexer, and the decider/prover. That stack is a prerequisite the product repository documents (build it per docs/BUILDING-THE-STACK.md, run it per docs/RUNNING-THE-STACK.md); it is not vendored into SubEtha and not documented further on this site.

One SubEtha-authored contract participates: PermitBurner (Apache-2.0), the helper that makes the gasless permit path atomic — it grants the allowance from the payer’s EIP-2612 signature and executes the burn transfer in a single transaction.