Umbra (@Umbra_Inference) header
Umbra (@Umbra_Inference) profile picture

Private AI inference, paid per request. x402 · USDG · signed receipts · nothing stored. No API key, no account, no logs.

Joined October 2026
Photo by Umbra (@Umbra_Inference)Photo by Umbra (@Umbra_Inference)Photo by Umbra (@Umbra_Inference)Photo by Umbra (@Umbra_Inference)Photo by Umbra (@Umbra_Inference)Photo by Umbra (@Umbra_Inference)Photo by Umbra (@Umbra_Inference)Photo by Umbra (@Umbra_Inference)Photo by Umbra (@Umbra_Inference)Photo by Umbra (@Umbra_Inference)
UPDATE · gateway 0.11.0 is live. Buybacks are back, on Uniswap v4. When UMBRA graduated from its launch curve to Uniswap v4, two things in the gateway paused by design: the automated buyback and paying in UMBRA. Both read their price from the curve, and the curve was gone. Today both read the v4 pool, and the first buyback on it is done. What shipped: • Price source: the ETH/UMBRA v4 pool, read straight from PoolManager storage, times the WETH/USDG pool for dollars. No oracle, no API. • Buyback route: USDG → WETH (v3) → ETH → UMBRA through the Universal Router (V4_SWAP: swap exact in, settle all, take all) → 0x…dEaD. • Exact quote: the output is found by simulation before sending, so the pool's hook fee (3% on output) and the price impact are inside the slippage floor. No surprises, no stuck runs. • Pay in UMBRA is available again: same Permit2 rail, same fee share destroyed with burn() right after settlement, same holder tiers. • The executor burns exactly what it bought. UMBRA received as payment stays in the fee wallet, like USDG does, and is subject to the same 50% policy. First run on the new route, 2026-10-21: • 0.2547 USDG → 0.00010375 ETH → 16,898.00 UMBRA bought on v4 → burned • burn tx robinhoodchain.blockscout.co… • buy tx robinhoodchain.blockscout.co… • dead address now 17,868,926 UMBRA, 1.787% of supply Policy unchanged: 50% of fees buy and burn, 50% fund operations, executed inside the enclave, parameters in the measured compose (0.11.0: 1e00ef0d…). The ledger is the chain, readable at /revenue. umbra-inference.app CA 0x4d3ace48b1590bc4c5dd46f05237c133be564eb3
28
2
3
55
6,410
Update 0.23.0: your temperature now reaches the model. The receipt says whether it took it. Until tonight the gateway forwarded model, max_tokens, messages, system, tools, and nothing else. temperature, top_p, stop, seed: read, dropped, silently. Not a privacy feature, an omission. Fixed. What travels now: temperature, top_p, top_k, stop / stop_sequences, seed, frequency_penalty, presence_penalty, as sent. max_completion_tokens is accepted as max_tokens. user and metadata still never leave the enclave. The full list, both directions, is published at /.well-known/umbra.json → payment.request. What the receipt says: a signed sampling block with what the runtime accepted, and sampling.ignored with what it refused. Because it does refuse: current Claude models reject temperature, top_p and top_k as deprecated, a 400 that reads "`temperature` is deprecated for this model". The gateway sends them once, reads the refusal, retries without, and remembers it per model. Stop sequences work on every Claude model. The sealed runtime takes all of them, seed included. Tonight on the enclave, "count from 1 to 20" with stop ["7"]: • Sonnet 5.5 → "1 2 3 4 5 6", stop_sequence, sampling {stop_sequences: ["7"]} • Haiku 5.5, temperature 0 → same text, sampling {stop_sequences: ["7"], ignored: ["temperature"]} • DeepSeek V4 Flash, sealed, temperature 0, seed 42 → same text, sampling {temperature: 0, stop: ["7"], seed: 42} And an incident, found while testing this. 0.22.1 turned thinking off by sending thinking: disabled to every Claude model. Three of them do not accept that spelling: Sonnet 5.5 wants between_tools, Opus 5.5 and Fable 5.1 cannot turn it off at all and want adaptive with an effort level. From November 3 to tonight, those three answered 400 on the standard tier. Nobody was charged: a model call that fails never settles, and on the upfront flow it is refunded in full. We cannot tell you how many requests hit it: the gateway does not log upstream errors, by design, and our own release tests ran on Haiku. 0.23.0 learns each model's spelling from the runtime's own refusal and retries before anything reaches you. Verified tonight on all six Claude models, streams included. Compose 32be3c91…, tarball 202d75e9…, same enclave, same receipt key. Receipts keep verifying with SDKs 0.4.0: the schema gained one optional field, nothing else moved. umbra-inference.app robinhood:0x4d3ace48b1590bc4c5dd46f05237c133be564eb3
1
1
5
227
483,333 payments in October. Average size: 3.9¢. Payments that small only make sense for agents. 1,158 merchants are already accepting them on PayAI Facilitator. facilitator.payai.network
13
18
1
69
2,118
What the payment processor sees. On Base and on @solana , a facilitator verifies your payment and broadcasts it. Ours is @PayAINetwork . People ask what it learns about a request. Here is the whole exchange, read from the client library the gateway uses. Two HTTP calls from the enclave to facilitator.payai.network, per paid request on those rails. POST /verify, then POST /settle. Same body for both: • x402Version: 2 • paymentPayload: the accept you chose (scheme, network, amount, asset, payTo, maxTimeoutSeconds) and your signed payload. On Base: the EIP-3009 authorization, from, to, value, validAfter, validBefore, nonce, and its signature. On Solana: the signed transaction, with the facilitator's key as fee payer. • paymentRequirements: the same accept, as the gateway issued it. Two answers: { isValid, payer }, then { success, transaction, network, payer }. That is everything it receives. Not in it: your prompt, the model, the answer, the token counts, your IP (the connection comes from the enclave). What it sees is what a payment processor needs to see: who pays, whom, how much, by when. Your client may copy the resource block into the payload, our public URL and its public description; nothing of yours. On eip155:4663 those two calls do not leave the process. The same verify and settle run inside the enclave, with the gateway's own wallet paying gas. The receipt tells you which happened: payment.facilitation is "PayAI" on Base and Solana, "gateway" on USDG. Both are honest answers to the same question: who touched the money, and what they could read while doing it. umbra-inference.app robinhood:0x4d3ace48b1590bc4c5dd46f05237c133be564eb3
1
9
367
7.13 UMBRA for a tenth of a cent. Here is the arithmetic. A 402 tonight, 00:38 UTC, for a 16-token request: ceiling 0.0001 USD, or 7.129372 UMBRA at 0.0000147278 USD per UMBRA. No oracle, no price API. The gateway computes that number inside the enclave from two reads of the chain. Read one, UMBRA per ETH. The ETH/UMBRA pool is Uniswap v4, and v4 keeps every pool inside one contract, the PoolManager. There is no pool contract to call, so the gateway reads one storage slot: keccak256(poolId, 6), and unpacks sqrtPriceX96 from its low 160 bits. Tonight tick 189196, 164,549,430 UMBRA per ETH. Read two, dollars per ETH. slot0() of the WETH/USDG v3 pool: 2,423.37. Divide: 0.0000147 USD per UMBRA. Cache it 60 s. Add 5% against staleness, because the price is signed into a ceiling that lives up to 15 minutes. 0.0001 USD → 6.79 UMBRA at spot, 7.13 quoted. We redid both reads from a laptop at block 106,324,407: 0.0000147273. The gateway's figure, read up to 60 s earlier: 0.0000147278. 0.003% apart, the cache. The exact formulas are on the card; the two addresses are in the code. Then the usual: settled through Permit2, upto for what the model produced, exact for the ceiling. And 4% of what settles, 2% at the 100k tier, 0% at 1M, is destroyed with the token's own burn() right after settlement. That is a Transfer to 0x0, counted apart from the dead-address burns. The receipt carries umbra_usd and fee_burn, so the price you were quoted is the price you can check. One limit, said plainly: robinhood:0x4d3ace48b1590bc4c5dd46f05237c133be564eb3 is offered on the authorization flow only. Streams and long requests settle first and refund after, and the refund path is USDG. umbra-inference.app
1
4
250
Six hosts. Your prompt reaches one. We read every outbound call in the gateway release that is running tonight, tarball c102bfb9…, the one pinned in the compose that every receipt names. Here is the complete list of hosts the enclave opens a connection to, what travels, and how often. 1. api.anthropic.com. Standard tier. Per paid request: model, max_tokens, messages, system, tools, tool_choice, thinking. Nothing about the payment, no wallet, no end-user id. The IP it sees is the enclave's. 2. inference.phala.com. Sealed tier. The same body in the OpenAI dialect, per paid request. Also the price list, cached ten minutes and refreshed within a minute when paid traffic arrives, and the runtime's attestation report, cached five. 3. rpc.mainnet.chain.robinhood.…. The chain. Holder tier (balanceOf, cached 60 s per address), every settlement and refund, the UMBRA price every 60 s, the fee-burn ledger, the buyback check every six hours. Addresses, amounts, signed authorizations. One node, no fallback, by design. 4. facilitator.payai.network. Base and Solana only. Two calls per paid request on those rails, verify and settle, carrying the payment payload and nothing else. Next post. 5. api.mainnet-beta.solana.com, and its devnet twin for the test rail. One question at boot: does our USDC account exist. Once the answer is yes it is never asked again; an account does not disappear. Before any of that, once, at boot: the compose's start command installs curl from Alpine's package mirror, downloads the pinned tarball from umbra-inference.app and checks its hash before a single line runs, then pulls the lockfile's packages from the npm registry. Three hosts, once, before node starts. After that the six above are all the process ever calls. Not on the list at run time: umbra-inference.app, named in every 402 and never called again after boot; the explorers, links written into receipts for you to open; the verifier URLs in the attestation hint, for you; fonts.googleapis.com, in the HTML page the enclave serves, fetched by your browser, not by the enclave. The KMS that derives the receipt key is a local socket, not a network host. Plainly: there is no egress firewall in the compose. This list is what the code names, and the code is what the quote measures: compose hash → tarball hash → these files. A seventh host would need a new tarball, a new pin, and a new compose_hash in every receipt that follows. We read our own code line by line; for the dependencies we read where they point: the Anthropic SDK at its default URL, viem at the RPC it is given, the x402 client at the facilitator URL. umbra-inference.app
2
9
323
The runtime moved overnight. The receipt noticed. Yesterday at 22:17 UTC the sealed runtime's public report said: source commit 22d08e24…, workload keyset sha256:df2a7cd6…. Today at 14:14 UTC it says: commit 77fac126…, keyset sha256:5a0bf911…. They shipped a release. Our gateway did not move: compose hash 24c16b64… both times, same key. Where that lives in your receipt: attestation.model_runtime, four fields. workload_keyset_digest and source.commit, copied from the runtime's report. attestation_url, where to read the report yourself. upstream_receipt_id, the runtime's own receipt for this exact response. Two attestation chains, two release cadences. Ours changes when we release, and every compose that ever signed a receipt is archived on the site. Theirs changes when they release. We do not control it; we record it. Sort your receipts by source.commit and you have their release history as seen from our side, with the minute it reached us. How fresh. The gateway re-reads the runtime's report every five minutes, so the block in a receipt is at most five minutes old. The upstream_receipt_id is per response, never cached. And the keyset carries its own expiry, not_after 2026-12-04 00:50 UTC: a receipt names the keyset that was valid at served_at, not whatever is current when you check. What to compare: source.commit against github.com/Dstack-TEE/privat…, the keyset digest against inference.phala.com/v1/aci/a…, and two of your own receipts from either side of last night. umbra-inference.app
2
5
201
Two receipts per answer. Every sealed-tier answer leaves two signed receipts, not one. Ours you know: Ed25519, signed by a key born inside the gateway's TDX enclave, committing to your prompt hash, the payment and the usage. The runtime's is the one we had never shown. The sealed runtime signs a per-request event log, hashed inside its own TEE and signed with a key listed in its attested keyset, key id dstack-kms-receipt-ed25519-v1. We fetched the one for a request made today at 14:14 UTC: request.received body_hash sha256:3db7c03a… middleware.forwarded body_hash sha256:10a7b890… route.selected the backend that served it request.forwarded body_hash sha256:df763c69… upstream.verified result verified · required true · session db60e58d… response.received body_hash sha256:4feb735e… response.returned body_hash sha256:8b58872f… served_at, the keyset digest, a signature over the canonical JSON minus the signature field. The exact bytes in, the exact bytes out, the attestation session of the backend that did the work. "Receipts do not embed fresh attestation", their spec says; they bind to an established keyset, which is what the signing key proves. How the two link: our receipt carries attestation.model_runtime.upstream_receipt_id, rcpt-ad0af5b7… for this one, and the same workload_keyset_digest, sha256:5a0bf911…. One thing to say plainly. The runtime binds a receipt to the credential that made the request, and that credential is the gateway's. Fetch it without it and you get 401, "this receipt is owned". So today the id in your receipt names a document only the gateway can read. We pulled this one; you cannot. umbra-inference.app
1
7
193
The menu is in the code. The prices are not. The sealed runtime we use lists 22 models tonight. We sell eight of them. The eight ids are a list in lib/upstream.js, inside the release tarball pinned by sha256 in the compose, whose hash is in the TDX quote. Adding a model is a release: a new compose hash, a new attestation, a new archived compose. You can diff two releases and see the menu change. The prices are not in that file. The gateway reads them from the runtime's public /v1/models and re-reads every ten minutes; ours shows them unchanged, with the fee as a separate field, 400 bps. If the runtime moves a price, our quotes follow within ten minutes, with no release and no new hash. The six Claude prices are list prices, written in the code, and change the hash when they change. Tonight's eight, $ per million tokens in · out: DeepSeek V4 Flash 0.44 · 1.32 Qwen 3.8 27B 0.20 · 2.50 Llama 3.3 70B 2.00 · 2.00 GPT-OSS 120B 0.15 · 0.60 Qwen 3.5 397B-A17B 0.55 · 3.50 Kimi K3 3.00 · 15.00 Gemma 4 31B 0.15 · 0.46 GLM 5.3 Flash 0.15 · 0.50 What pins a price is the receipt: tokens in, tokens out, the amount, the fee in bps. The price you paid is a division you can do. And since 0.4.0 the SDK does it before signing: it keeps the list for ten minutes and refuses a 402 whose ceiling the list cannot explain. umbra-inference.app
2
1
8
301
Read the hostname. curl -sI umbra-inference.app/v1/model… 307 location: 1f88acf14761998af024e7b42b06… The forty hex characters before -8402 are not a random subdomain. They are the app_id: the same string in attestation.gateway of the well-known document, in the TDX quote the hardware signs, in keys.json next to the receipt key, and in every receipt signed since October 22. 8402 is the port the gateway listens on. We did not register that name. Phala's dstack assigns it to the application, and the application is identified by the measured deployment the KMS derives our receipt key for. A release changes the compose hash and keeps the name. A new CVM changes the name, which is exactly what happened on October 22: 3675ce94… retired at 15:36 UTC, 1f88acf1… current from 15:59, both in keys.json. So the hostname your client talks to and the identity your receipts are signed under are one string, readable without any tooling. The SDK reads the Location header once, without a body, and sends every request to that host directly. Compare three strings: the host in your client's log, attestation.gateway.app_id in the receipt, the app_id in keys.json. If they are not the same forty characters, stop. umbra-inference.app
1
8
179
Seven team burns we had not listed yet, 6.58M UMBRA out for good. Oct 21 · 389,145.54 Oct 21 · 393,182.33 Oct 21 · 829,693.12 Oct 22 · 688,688.74 Oct 24 · 1,705,157.91 Oct 24 · 1,314,279.86 Nov 3 · 1,257,543.55 All seven from the team allocation to the dead address, 6,577,691 robinhood:0x4d3ace48b1590bc4c5dd46f05237c133be564eb3 between October 21 and last night. Nothing minted, nothing coming back. Running total since launch: 29,250,716 UMBRA out of circulation, 2.93% of supply. Verify in two calls on the contract: balanceOf(0x…dEaD) and totalSupply(). Both only move one way. The seven transactions are on the card. umbra-inference.app CA 0x4d3ace48b1590bc4c5dd46f05237c133be564eb3
1
2
6
246
The model that answered. A receipt has a `model` field. It is not the model you asked for. It is the model string the runtime returned, copied as is. Most of the time the two are the same word, and then that is all you see. When they differ, the receipt says so, with two more fields: `requested_model`, what you sent, and `billed_as`, the price you paid. Why they can differ. On the standard tier the gateway opts into the provider's server-side fallback: if the model you named declines a request for policy reasons, the provider may run it on its default substitute instead of handing back a refusal. On the sealed tier the runtime names the model that served the request, and we do not edit it. The billing rule, in the code you can hash: usage is priced on the model that answered when it is on the price list, on the model you requested otherwise. In both cases, never above the ceiling you signed. If the substitute is dearer and the meter runs past your signature, the ceiling is what settles, and the receipt shows cost.usage above cost.settled. The difference is ours. Tonight, three paid requests against the live enclave: Claude Haiku twice, DeepSeek V4 Flash once. Each receipt's `model` equals the request, so none carries `requested_model`. A receipt without that field is a receipt where the model you named did the work. How often does a fallback happen? We cannot tell you. We keep no receipts. Yours says: one field to compare, result.receipt.receipt.model against what you sent. umbra-inference.app
2
7
251
Same here: the 402 is free and nothing is signed until the client agrees. We just gave the look a rulebook. The gateway publishes how a quote is computed and where it pays to, so walking away is a check the SDK runs, not a judgment call the agent has to make.
same reason quotes are free on our endpoints. the agent gets to look at the 402 and walk away
1
1
5
265
Should the agent have paid at all? Gateway 0.22.1, SDKs 0.4.0. Agents are getting accounts: @brian_armstrong opened a sign-up page for them at Coinbase this week. @PayAINetwork said the next part out loud: now they need things to buy, and x402 makes every API a storefront. The same week, @BlockRunAI gave ten frontier models real USDC wallets on @solana . Seven got scammed at least once. PayAI settled every one of those payments and put it well: the rails did their job, the hard part is whether the agent should have paid at all. A scam, on x402, is a 402 you should not have signed. So the check now runs before the signature. The gateway publishes its quote rule: how a ceiling is computed from your body, your max_tokens, the public price list and the fee, and where the money goes. The SDKs read it and refuse any 402 that does not follow it. Tested tonight against the live enclave, same request, seven variants: as quoted → signed ceiling ×3 → refused ceiling +1 above the bound → refused pay_to swapped → refused fee 900 bps, published 400 → refused model not on the price list → refused a Sonnet-sized quote for a Haiku body → refused Nothing is signed on a refusal; the check that passed rides on the result. The price list comes from the same gateway, which is the point: that gateway is attested, and the site's eight checks tie its code to the hardware. Caps per call, per hour and per task were already there. Now the agent also knows a fair price before it pays one. Also in 0.22.1: thinking is off unless you ask for it. On a paid API max_tokens is the price ceiling; a model that thinks by default could spend a 60-token cap on thinking and hand back an empty, billed answer. Now it does not, and thinking in the Anthropic dialect passes through when you want it. Rails, for the record: USDG on eip155:4663 is settled by the gateway itself inside the enclave; USDC on Base and Solana settles through PayAI's facilitator, the same one the benchmark ran on. @CoinbaseDev 's x402 facilitator reached @monad this week; it is not on our list yet, and a chain joins the day a facilitator we can use speaks it. umbra-inference.app robinhood:0x4d3ace48b1590bc4c5dd46f05237c133be564eb3
We added a new sign up page for Agents. We're building Coinbase to be the most AI-enabled financial account. Give it a try: coinbase.com/agents
4
2
2
15
1,516
Cached tokens now cost a tenth. Gateway 0.21.1. Agents send the same system prompt and the same tool definitions on every call. The model provider caches that prefix and bills a cache read at 10% of the input price. Until today the gateway billed every input token at the full price, cached or not. Now it bills the way the provider bills: cache_read at 0.1×, cache_write at 1.25× (2× for the one-hour cache), the rest at the input price. The receipt splits them out, tokens.cache_read and tokens.cache_write, and names the multipliers in cost.cache. Nothing to configure: mark your blocks with cache_control in the Anthropic dialect and it passes through. Measured tonight on the enclave, claude-haiku-5-5, a 12,464-token system prompt, two calls: call 1, the write: 12,464 cache_write + 18 input, 116 out → 0.001651 USDG call 2, the read: 12,464 cache_read + 15 input, 200 out → 0.000231 USDG Under yesterday's rule the second call would have cost 0.001402. Six times less, same answer, same receipt, signature valid. One thing we got wrong for forty minutes. 0.21.0 billed the write at 1.25× but still quoted the ceiling at 1×, so a first call with a long cached prompt came out above what you had signed, and the gateway asked the chain for more than the authorization. The chain said no, nothing was charged, the client got a 402. 0.21.1 quotes a request that carries cache_control at 1.25× on the input, and settles at most the ceiling on every asset, with cost.usage and cost.settled both in the receipt when they differ. Also in this release: if the provider serves a fallback model that is in our catalogue, the bill is at that model's price and the receipt names both; the sealed price list refreshes at most every 60 s before a quote; a system prompt given as blocks now counts in the estimate. umbra-inference.app
2
1
1
11
550
When the model provider stumbles, who pays for the second try? Nobody. Here is the exact policy, tier by tier. Standard tier (Claude): the gateway keeps the provider library's defaults. Up to two retries on 408, 409, 429, any 5xx and connection errors, backoff from 0.5 s doubling to 8 s with jitter, a ten-minute ceiling, the provider's own x-should-retry header overrides the list, and a retry-after it sends is honoured as sent. Sealed tier (open models in GPU enclaves): one request, no retry. A failure is a typed 502 at once. What it costs you, both tiers: Authorization flow (short requests): nothing has moved yet. Retries happen before settlement. If they outlive your signature's window, settlement is refused and the request is free. Upfront flow (long requests, streams): the ceiling was settled first. Retries are invisible. If every attempt fails, the full ceiling comes back on chain, transaction in the body. The receipt is built from the usage of the answer that came back. A failed attempt has no usage, no receipt, no line anywhere. Two things to know: a retried request shows up in the timing object as a slow model_ms, nothing says "retried"; and the retry policy is the library's, kept on purpose, not something we tuned. umbra-inference.app
1
7
372
Every transaction the gateway sends comes from one key. Settlements, refunds, buybacks, fee burns: one wallet, which is also the address you pay to. One account object shared by every module, and the nonce manager hands out consecutive nonces across whatever is in flight. Tonight we fired three long requests at the enclave at the same instant. Each one settles its ceiling before the model runs and refunds the unused part after. Six transactions from one wallet in about three seconds: settlements: nonces 218, 220, 219, blocks …456, …459, …459 refunds: nonces 223, 222, 221, blocks …478, …477, …475 No gap, no collision, no waiting for the previous one to mine. Each request: 0.001074 USDG settled, 0.000974 refunded, 0.000100 kept, which is what the answer cost. Refunds landed 242, 262 and 291 ms after the last token. Why it is built this way: a refund still in the mempool must not collide with the next settlement. One "nonce too low" would stall every send after it. That is also why the gateway talks to one RPC node and never falls back to a second: the nonce is managed locally on top of what the node reports, and two nodes that disagree break the sequence. We learned that on 0.20.0, for twenty minutes. The wallet's nonce is public. It is a running tally of every send the gateway has ever made: 224 tonight. umbra-inference.app
1
8
368
Three clocks touch every Umbra payment, and only one of them can refuse it. Yours. The 402 quotes a window, 126 seconds for a short request. Your SDK writes the deadline as its own clock plus that window, into the authorization you sign. Nobody else sets it. The gateway's. Before it tries anything, it rejects an authorization that expires within six seconds of its own now: a 402, nothing moved, nothing charged. A laptop two minutes slow signs deadlines that are already past. A laptop two minutes fast signs a longer window than we quoted, and we do not shorten it: the chain honours the deadline you signed, not the one we offered. The nonce still makes it single-use. The chain's. The token contract settles only while the block timestamp is below your deadline. That is the one check that can refuse the transfer, and it runs where we cannot touch it. One more, for the record: the enclave's clock is the host's. TDX attests code, not time. So issued_at in the receipt is a claim; the settlement transaction's block is the anchor. Yesterday's receipt says 15:09:35.557Z, its block says 15:09:35. Tonight, 02:23 UTC, from one laptop: laptop, gateway and latest block within one second of each other.
1
1
6
408
Here is what one of those looks like today, for an LLM answer instead of an article. A real call, this afternoon, 15:09 UTC. No account, no API key, no checkout page. The agent POSTs the prompt and gets a 402 back with the price: a ceiling of 0.000100 USDG for 60 output tokens, valid 126 seconds, five networks to choose from. It signs that ceiling as an x402 authorization and sends the same request again. The answer comes back with a signed receipt: 22 tokens in, 43 out, 0.000100 USDG settled, zero bytes retained, the enclave's compose hash inside. The settlement is one transfer on chain, payer to gateway, in the next block. A hundredth of a cent for one sentence. Nothing to sign up for, nothing to cancel. The seller never sees a card, the buyer never opens a tab. The pieces are plain x402. On USDG (eip155:4663) the gateway verifies and settles itself, inside the TDX enclave. On Base and Solana it accepts USDC through @PayAINetwork facilitator, the one in the post above this one. Either way the receipt says which. umbra-inference.app robinhood:0x4d3ace48b1590bc4c5dd46f05237c133be564eb3
With stablecoins and x402, businesses can accept usage-based micropayments, and reduce friction to checkout. E.g. you shouldn’t need a monthly subscription to read one article - your agent can just fetch it for you, and pay the business for that one article.
2
8
365
Where the time goes. Gateway 0.20.1. Two things shipped today. First, every paid answer now tells you where its time went: a Server-Timing header and a timing object, in milliseconds. verify_settle: your signature checked and the ceiling settled on chain, mined, before the model runs. first_token and model. refund: the unused ceiling going back. total, since the request reached the gateway. On a stream the header leaves before the first token, so it carries verify_settle and the rest rides the final event. Not signed: it is operational, not part of the receipt. Second, the part we could make faster, we did. A settlement or a refund used to cost five RPC round trips to the chain: nonce, gas estimate, latest block, priority fee, broadcast. The gateway now sends with a fixed gas limit and fees from a 30 second cache: nonce and broadcast, two round trips. Unused gas is not charged. If a node rejects the parameters, the same send is retried the slow way. Measured on the enclave this afternoon, three streams: the gap between the last token and the receipt, which is the refund, was 224, 313 and 260 ms. Yesterday evening, same path, five round trips: 657 ms. This morning, with the chain's RPC having a bad day, it was 5 to 12 seconds, which is what started this. One thing we tried and withdrew the same day: a fallback RPC. The second node was behind on our wallet's pending nonce, one settlement went out with a nonce too low, and 0.20.0 never got a published compose. 0.20.1 is one RPC with a timeout, on purpose. The timing object is there so the next bad day is a number, not a guess.
1
4
386