# Compute — machine-paid confidential compute

Persistent Docker workloads, one dedicated confidential VM per deployment, paid in USDC over
x402. No signup, no API key: the paying wallet owns the deployment and is the
only identity. Every machine is confidential compute (Intel TDX) with a
remote-attestation quote you can verify.

## Plans (live prices — reread before paying)

- `compute.starter` — 1 vCPU, 2 GB RAM, 20 GB disk, TEE (Intel TDX): from $0.294/hour. Prepay 1-720 hours. 100 GB egress included.
  Per hour by how many hours ONE payment buys, per network:
    `base`: 1h $0.30/hr · 6h $0.295/hr · 24h $0.294/hr · 720h $0.294/hr
    `polygon`: 1h $0.305/hr · 6h $0.296/hr · 24h $0.294/hr · 720h $0.294/hr
    `solana`: 1h $0.298/hr · 6h $0.294/hr · 24h $0.294/hr · 720h $0.294/hr
- `compute.standard` — 2 vCPU, 4 GB RAM, 40 GB disk, TEE (Intel TDX): from $0.587/hour. Prepay 1-720 hours. 250 GB egress included.
  Per hour by how many hours ONE payment buys, per network:
    `base`: 1h $0.593/hr · 6h $0.588/hr · 24h $0.587/hr · 720h $0.587/hr
    `polygon`: 1h $0.599/hr · 6h $0.589/hr · 24h $0.587/hr · 720h $0.587/hr
    `solana`: 1h $0.591/hr · 6h $0.587/hr · 24h $0.587/hr · 720h $0.587/hr
- `compute.pro` — 4 vCPU, 8 GB RAM, 80 GB disk, TEE (Intel TDX): from $1.173/hour. Prepay 1-720 hours. 500 GB egress included.
  Per hour by how many hours ONE payment buys, per network:
    `base`: 1h $1.179/hr · 6h $1.174/hr · 24h $1.173/hr · 720h $1.173/hr
    `polygon`: 1h $1.185/hr · 6h $1.175/hr · 24h $1.173/hr · 720h $1.173/hr
    `solana`: 1h $1.177/hr · 6h $1.173/hr · 24h $1.173/hr · 720h $1.173/hr

Prices are read from the catalog at request time: `GET https://api.agentgates.ai/api/compute/catalog`.

## Why buying more hours at once is cheaper per hour

Every payment costs us one on-chain settle, and that fee is the same whether
you buy one hour or seven hundred. So the price you are quoted is
`(machine cost + settle fee ÷ hours this payment buys) × margin`, and buying
more hours in a single payment carries less of the fee per hour. The fee is
read from live gas at the moment you ask, never a constant, so the quote moves
with the chain. Every `402` carries the exact number you will pay.

The settle costs a different amount on each chain, so THE PRICE IS PER
NETWORK: every entry of the `accepts` array carries its own
`maxAmountRequired`, and a network is offered only where its own price
clears its own minimum. What another chain's gas is doing never moves the
price on yours.

Pay-as-you-go works the same way: each draw is one settle, so a bigger grain
is cheaper per hour than an hourly one. Grains on offer are in
`GET /api/compute/catalog` under `payg.grainChoicesHours`, and each plan's
`pricing` array is the full ladder.

## Buy in one pass

1. `POST https://api.agentgates.ai/api/compute/deployments` with JSON
   `{"plan": "<plan id>", "hours": <int>, "compose": "<docker-compose.yml as a string>", "env": {"KEY": "value"}, "name": "optional"}`
   (`"image": "nginx:alpine"` may replace `compose` for a one-container run;
   it publishes host port 8080 in front of the container's port 80, or
   `"port": <n>` for a different one. Your own `compose` must publish a port
   too — a container that publishes nothing has no address, and the machine
   runs and bills with no way to reach it. The endpoint is the published HOST
   port: `https://{appId}-8080.{gateway}`.)
2. The reply is `402` with an `accepts` array (x402, scheme `exact`).
   Networks offered: `base` (USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, pay to 0x90eb67757bed72a825b45f4b50828a635474cab0), `polygon` (USDC 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359, pay to 0x90eb67757bed72a825b45f4b50828a635474cab0), `solana` (USDC EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v, pay to A8QmYWssCDVHRJRfYgKY1H7EgzG6XJWNKim2YbzAEkcv).
3. Sign an EIP-3009 `TransferWithAuthorization` for `maxAmountRequired`
   USDC to `payTo` (EIP-712 domain in `extra`), and retry the same POST with
   `X-PAYMENT: base64({"x402Version":1,"scheme":"exact","network":"<network>","payload":{"signature":"0x…","authorization":{"from","to","value","validAfter","validBefore","nonce"}}})`.
   On a `solana` / `solana-devnet` leg, build a transaction of exactly
   [ComputeBudget SetComputeUnitLimit, ComputeBudget SetComputeUnitPrice,
   spl-token `TransferChecked` of `maxAmountRequired` USDC (`asset`, 6
   decimals) to `payTo`'s associated token account, optional Memo], set the
   fee payer to `extra.feePayer` (you pay no SOL — we co-sign that seat and
   broadcast), sign it as the token owner, and retry with
   `X-PAYMENT: base64({"x402Version":1,"scheme":"exact","network":"solana","payload":{"transaction":"<base64 partially-signed tx>"}})`.
   Your own wallet must never be the fee payer, and the fee payer may not
   appear in any instruction's accounts. Sign against a FRESH blockhash —
   the payment is only good for ~60 seconds.
4. Settlement is confirmed on-chain, then your machine provisions:
   `202 {"deploymentId", "status": "provisioning", ...}`. Poll
   `GET /api/compute/deployments/{id}` until `status: "running"` —
   `endpoint` and `attestation` are then live. The clock starts at
   `running`, not at payment.

x402 v2 works too: every `402` also carries a `PAYMENT-REQUIRED` header
(CAIP-2 networks), and the retry may send `PAYMENT-SIGNATURE` instead of
`X-PAYMENT`. The official client (`@x402/fetch` on `@x402/core` 2.x) refuses
any payment over $1 by default: for more hours, a larger plan or a balance
rung over $1, raise its cap, e.g. `wrapFetchWithPaymentFromConfig(fetch,
{ schemes, spendControls: { maxAmountPerPayment: "$50" } })`.

Your env var VALUES are sealed to the machine's enclave key and never stored;
if a first provision attempt has to be retried by the worker, the retry runs
without them, so keep secrets re-settable or bake them into your compose.

Add `"aliasTarget": "<service>:<port>"` (a service in your compose) to serve
on a dedicated domain instead of the raw gateway hostname: the reply carries
`aliasHost`, and `endpoint` becomes `https://{aliasHost}` once its
certificate is issued INSIDE your machine — your compose runs with one added
ingress container that holds your domain's certificate in the machine; we
never hold the key and no proxy of ours ever sits in front of your traffic.
Omit `aliasTarget` and nothing is added.

## Pay as you go (the agent loop)

On a plan whose `minHours` is 1 you never have to prepay a block. The
billing grain is one hour
(a config value — reread this page, not a cached copy), and renewals extend
from the clock's current end. So the
minimal always-on loop is:

1. Buy 1 hour (`"hours": 1` in the create above). The clock starts at
   `running`.
2. Watch `expiresAt` on `GET /api/compute/deployments/{id}`.
3. Shortly before it passes, `POST /api/compute/deployments/{id}/renew`
   `{"hours": 1}` with a fresh x402 leg — the renewal stacks on the end of
   the clock, so renewing early never loses time.
4. Repeat. Stop paying and the machine stops at `expiresAt`; you owe
   nothing further, ever (nothing is postpaid). It then sits in `grace`
   (data kept, renew still works) for as long as it was paid for, at least
   an hour and at most 72 hours, before it is destroyed.

Each settle is an on-chain USDC transfer, so pay each hour right before it
starts and you are never ahead of your usage by more than the hour you are
in — and never behind it, because the machine only runs on settled time.

Humans get the same economics without running a loop: the dashboard's
pay-as-you-go allowance pre-signs one authorization per hour in a single
wallet ceremony (each valid only inside its own hour window, cancelable,
capped), and this API draws them on the same schedule. Agents do not need
it — the loop above is the whole contract.

## If you lose the answer

A timeout or a dropped connection after you send `X-PAYMENT` is safe to retry.
Send the SAME `X-PAYMENT` to the SAME endpoint: the receipt is keyed by your
authorization's own nonce, so the retry returns the machine that payment
already bought, marked `"alreadyPaid": true`, and charges nothing. Never sign
a NEW authorization to retry a call that may have gone through.

If a payment is spent and we cannot match it to a receipt, you get
`409 payment_already_spent` instead of a fresh price. List your machines with
the owner call below to find what it bought.

## Prepay once, then buy many times

🚨 THIS IS A PREPAID BALANCE. You send USDC FIRST and spend it down; nothing
is loaned. Every pay-per-purchase call carries one on-chain settle; a balance
pays that settle ONCE, for the whole amount, and every machine or renewal
after it is a draw with no chain write.

1. `POST https://api.agentgates.ai/api/compute/credits` with `{"amountUsd": 5}` (`1`,
   `5`, `25` or `100`). The `402` asks for the amount plus the one
   delivering settle. Pay it like any purchase below.
2. From then on `POST /api/compute/deployments` or `.../renew` with
   `X-Wallet-Auth` (or an API key) and NO `X-PAYMENT` draws the balance —
   charged at the cheapest offered total, since a draw settles nowhere.
3. `GET https://api.agentgates.ai/api/compute/credits` reads the balance and ledger.

A balance never expires, is not refundable, is per wallet and never
transferable. A balance short of a purchase answers the ordinary `402`.
Egress top-ups still pay per purchase for now.

## API keys (optional, for software that cannot sign wallet messages)

The wallet stays the only account. A compute key (`agc_…`, distinct from
inference's `ag_…`) is a bearer handle the wallet mints on its own prepaid
balance:

- `POST https://api.agentgates.ai/api/compute/keys` with `X-Wallet-Auth` and
  `{"name": "my-agent"}` — mints; the answer carries the secret ONCE.
- `GET https://api.agentgates.ai/api/compute/keys` with `X-Wallet-Auth` — active keys.
- `POST https://api.agentgates.ai/api/compute/keys` with `{"revoke": "<key id>"}` — kills
  a key instantly.

With `Authorization: Bearer agc_…` a key can: rent a machine and renew it
(draws on the balance), list the wallet's machines, and read the balance it
spends. It can NEVER destroy a machine, sign a payment, touch the wallet, or
list its sibling keys — the unspent balance is the most a leaked key can
spend, and Revoke ends it. Up to 20 active keys per wallet, one per agent.

## Owner calls (read, destroy) and renewals

Reads and destroys are wallet-signed, no session. Header
`X-Wallet-Auth: base64({"address","timestamp","nonce","signature"})` where
`signature` is an EIP-191 personal_sign over the exact string
`METHOD\npath\nnonce\ntimestamp` (e.g. `GET\n/api/compute/deployments/abc\n<nonce>\n<unix seconds>`).
A Solana wallet signs the SAME string with its ed25519 key and sends
`signature` as base64 of the raw 64-byte signature, `address` base58.
Nonces are single-use; timestamps must be within the server skew. A read or
a control call signed by any other wallet answers `404` `not found for this
wallet: sign with the wallet that paid for the machine`.

- `GET /api/compute/deployments` — your deployments.
- `GET /api/compute/deployments/{id}` — status, endpoint, expiry, invoices,
  live TEE attestation.
- `POST /api/compute/deployments/{id}/renew` `{"hours": <int>}` — x402-gated.
  Any wallet may pay a renewal with `X-PAYMENT`; only the owner reads, controls
  or destroys the machine. A renewal drawn from a prepaid balance, or one that
  opens a pay-as-you-go allowance, comes from the owner. Extends `expiresAt`.
- `POST /api/compute/deployments/{id}/egress` `{"gb": <int>}` — x402-gated
  egress top-up. Egress is metered, never postpaid: at quota exhaustion the
  machine suspends (stopped, not destroyed) until a top-up lands. A top-up is
  ONE payment and therefore ONE settle, so the per-GB price falls with the
  size of the block, exactly like the hourly ladder; each plan's sizes are
  listed above. A plan that quotes no top-up price is not selling egress yet
  and this answers `503 egress_unpriced` rather than inventing a number.
- `DELETE /api/compute/deployments/{id}` — destroys the machine. Remaining
  hours die with it.

## Control your machine

The machine's own wallet signs each of these, the same `X-Wallet-Auth`
header as a read (POST, over the exact path). Nothing here takes a payment.

- `POST /api/compute/deployments/{id}/restart` — restarts a running machine.
  The clock runs on.
- `POST /api/compute/deployments/{id}/stop` — stops a running machine and
  pauses its paid hours: the reply's `stop.pausedSeconds` is what is left. A
  stopped machine is kept 72 hours (`stop.deleteAt`),
  then deleted with its disk; a day before, we mail the address you proved on
  your account (`POST /api/account/email`). Renewing a stopped machine adds
  to its paused hours.
- `POST /api/compute/deployments/{id}/start` — starts it again. The paused
  hours run from the moment the machine is up (`stop.starting` while it boots).
- `POST /api/compute/deployments/{id}/resize` `{"plan": "<id>"}` — a new size.
  No payment and no refund: the time left converts at the new size's rate, so
  a bigger machine has fewer hours and a smaller one more. It restarts once.
- `POST /api/compute/deployments/{id}/update` `{"compose": "…"}` (or
  `{"image": "…", "port": 80}`) — a new setup on the same machine, optionally
  with `"env": {…}` (sealed to the machine; new values replace the old ones).
  It restarts with it and keeps its hours.

GPU machines run their block as started: no stop, resize or update. A
pay-as-you-go machine stops its allowance before a stop or a resize. Your
logs stay inside your machine: we cannot read them, and neither can anyone else
without your app.

## Prove it to your own customers

Every deployment has a public, shareable verify page:
`GET https://api.agentgates.ai/compute/verify/{deploymentId}` — the machine's compose
hash, OS image hash, and Intel TDX hardware quote, rendered from the
attestation chain the TEE itself produces. No login, no wallet.

## Lifecycle

`provisioning → running` (clock starts) `→ grace` (past expiry: machine
stopped, data kept, renew still works) `→ destroyed` (past the grace
window: as long as the machine was paid for, at least an hour, at most
72 hours). A machine you stop sits in `stopped` with its hours paused, and is
destroyed 72 hours after the stop unless you start it. Grace is a ceiling, not a reservation: an expired machine may be
destroyed sooner when a paying buyer needs the capacity. Renew before expiry
if the disk matters. Prepaid time is revenue at purchase; there are no refunds, of
anything, ever. A provision failure after payment is retried at our cost —
your clock has not started.

## Rules

No SMTP egress, no mining pools, no inbound SSH exposure, no sanctioned
wallets (every settlement is screened), no attacking, scanning or flooding
other systems. We never look inside a machine: memory and disk are encrypted
in the TEE and we hold no key to them; your logs stay inside it. Before we
stop or delete a machine for breaking a rule, we mail the address proved on
your account with the reason (a machine attacking others right now is
stopped first and you hear at once). When paid hours end you get a mail too.
The full text: https://agentgates.ai/terms#machines
