# 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.08/hour. Prepay 1-720 hours. 100 GB egress included.
  Per hour by how many hours ONE payment buys, per network:
    `base`: 1h $0.082/hr · 6h $0.08/hr · 24h $0.08/hr · 720h $0.08/hr
    `polygon`: 1h $0.084/hr · 6h $0.08/hr · 24h $0.08/hr · 720h $0.08/hr
    `solana-devnet`: 1h $0.093/hr · 6h $0.082/hr · 24h $0.08/hr · 720h $0.08/hr
- `compute.standard` — 2 vCPU, 4 GB RAM, 40 GB disk, TEE (Intel TDX): from $0.159/hour. Prepay 1-720 hours. 250 GB egress included.
  Per hour by how many hours ONE payment buys, per network:
    `base`: 1h $0.161/hr · 6h $0.159/hr · 24h $0.159/hr · 720h $0.159/hr
    `polygon`: 1h $0.163/hr · 6h $0.159/hr · 24h $0.159/hr · 720h $0.159/hr
    `solana-devnet`: 1h $0.172/hr · 6h $0.161/hr · 24h $0.159/hr · 720h $0.159/hr
- `compute.pro` — 4 vCPU, 8 GB RAM, 80 GB disk, TEE (Intel TDX): from $0.317/hour. Prepay 1-720 hours. 500 GB egress included.
  Per hour by how many hours ONE payment buys, per network:
    `base`: 1h $0.319/hr · 6h $0.317/hr · 24h $0.317/hr · 720h $0.317/hr
    `polygon`: 1h $0.321/hr · 6h $0.317/hr · 24h $0.317/hr · 720h $0.317/hr
    `solana-devnet`: 1h $0.33/hr · 6h $0.319/hr · 24h $0.317/hr · 720h $0.317/hr

Prices are read from the catalog at request time: `GET https://agentgates-backend.vercel.app/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://agentgates-backend.vercel.app/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 0xC5d451ADf4bED53E36fa7D091f6d3C3Edba4127D), `polygon` (USDC 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359, pay to 0xC5d451ADf4bED53E36fa7D091f6d3C3Edba4127D), `solana-devnet` (USDC BCSqoz1RT7L3ZFcMzRXnXGKNKBP5uuFPKiL73nuXSEJH, pay to Cj8uWYMBwq4vg1u1NyWHKmHbfBwxf2rRbyXsWtfpqJtN).
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.

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)

You never have to prepay a block. The billing grain is one hour
(a config value — reread this page, not a cached copy), every plan's
`minHours` is 1, 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`
   for 72 hours (data kept, renew still works) 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://agentgates-backend.vercel.app/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://agentgates-backend.vercel.app/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://agentgates-backend.vercel.app/api/compute/keys` with `X-Wallet-Auth` and
  `{"name": "my-agent"}` — mints; the answer carries the secret ONCE.
- `GET https://agentgates-backend.vercel.app/api/compute/keys` with `X-Wallet-Auth` — active keys.
- `POST https://agentgates-backend.vercel.app/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, renew, destroy)

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.

- `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,
  payer must be the owner wallet. 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.

## Prove it to your own customers

Every deployment has a public, shareable verify page:
`GET https://agentgates-backend.vercel.app/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). 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). Violations get the machine destroyed
with the reason on the record.
