The protocol spec
Record types, the reference graph between them, the state machine a contract moves through, and the rules that keep a settlement honest.
Record types
All records live in AT Protocol repositories. Cross-record references use com.atproto.repo.strongRef — a {$type, uri, cid} pointer that content-addresses the target.
All NSIDs carry the com.publicdomainrelay.temp.* prefix (pre-release namespace).
VM specification
Describes the desired machine: cpus, mem, disk, network, a role, cloud-init user_data, and an optional location.
cpus: 2
mem: 4G
disk: 40G
network: 500G
role: my-cool-role
user_data: |
#cloud-config
runcmd:
- [sh, -c, "echo hi"]
location: {country: USA, region: west}
RFP envelope
Wraps a strong reference to the VM record — this is what the requester publishes and providers discover.
payload:
uri: at://did:plc:alice/compute.vm/3mm3d
cid: bafyrei...vm
policy?: {uri: ..., cid: ...}
submitBid?: did:plc:alice#pdr_temp_market
signatures: [...]
Pricing payload
Cost, currency, frequency, prepay and a settlement URL — the economic terms of a bid.
cost: 0.10
currency: USDC
frequency: hourly
prepay: true
url: https://compute-contract.bob.example/receipt
Free pricing payload
Zero-cost bid — the reference default. A reason string and signatures.
cost: 0
reason: "provided at no cost"
signatures: [...]
Bid envelope
Binds a pricing payload to a specific RFP by strong reference.
rfp: {uri: ..., cid: ...}
payload: {uri: ..., cid: ...}
config?: {uri: ..., cid: ...}
submitAccept?: did:plc:bob#pdr_temp_market
signatures: [...]
Accept
The requester locks in a chosen bid, referencing both the RFP and the winning bid.
rfp: {uri: ..., cid: ...}
bid: {uri: ..., cid: ...}
payload?: {uri: ..., cid: ...}
submitEvent?: did:plc:alice#pdr_temp_compute_event
signatures: [...]
Receipt
The provider's proof of settlement — anchors the full contract chain behind one CID.
rfp: {uri: ..., cid: ...}
bid: {uri: ..., cid: ...}
accept: {uri: ..., cid: ...}
payload?: {uri: ..., cid: ...}
submitEvent?: did:plc:bob#pdr_temp_compute_event
cid: bafyrei...binding
signatures: [...]
Record graph
Every envelope points backward by strong reference — follow the arrows from a receipt and you can rebuild the whole contract.
State machine
A contract moves through one path to Settled, or exits early to Rejected if scoring finds no bid it will accept.
| State | Entered when | Next |
|---|---|---|
| RFP Open | Requester creates RFP + VM | Bidding |
| Bidding | Firehose fans the RFP out | Scoring (window closes) |
| Scoring | Bid window closes | Accepted or Rejected |
| Rejected | No bid passes policy | — terminal |
| Accepted | Requester creates Accept | Settling |
| Settling | Provider provisions + collects payment | Settled |
| Settled | Provider creates Receipt | — terminal |
Settlement
Two modes, both producing a signed market.receipt with the full contract chain and a proof CID binding accept → receipt.
- Requester creates
accepts.free. - Requester GETs
/free/receipt/{acceptsFree.uri}/{acceptsFree.cid}(full AT-URI, not bare uri) - Provider mints
receipts.freewith a proof CID. - Verification checks NSID, author DID, CID binding.
- Requester creates signed
accepts.x402. - Requester GETs the provider's receipt URL (constructed as
{bidPayload.url}/{acceptsX402.uri}/{acceptsX402.cid}) - Provider validates payment, returns
receipts.x402. - Optional
paymentMiddlewarecan handle processing.
VM lifecycle events
market.event records submitted via submitEvent, each referencing the receipt it pertains to. The accept-author check only applies to vm.delete events. Other events (vm.started, vm.onNetwork) are accepted from any authorized issuer.
| Event | Payload NSID | Trigger |
|---|---|---|
| vm.started | com.publicdomainrelay.temp.compute.events.vm.started | VM / container boots |
| vm.onNetwork | com.publicdomainrelay.temp.compute.events.vm.onNetwork | Guest gets an IP |
| vm.registerIdentity | com.publicdomainrelay.temp.compute.events.vm.registerIdentity | Guest registers DID identity |
| vm.delete | com.publicdomainrelay.temp.compute.events.vm.delete | Requester is done — accept author only |
On vm.delete, the provider checks the receipt is active and the issuer DID matches the accept author, calls computeProvider.destroy(), and fires onContractChange({ type: "terminated" }). Events are handled asynchronously — the provider responds 200 immediately.
Compute types
The RFP's compute.vm record is the one worked through above, but the same market, bid and receipt records carry other compute shapes too — a bidder can offer a Deno Worker instead of a VM. See the gateway page for both.
More on the protocol
How bidders and requesters each configure who they'll transact with.
Security & verificationSigned records, content-addressing, and how to verify a receipt yourself.
Workload Identity FederationHow a provisioned workload gets a scoped credential instead of a static secret.
Gateway serviceA hosted on-ramp if you don't want to run your own PDS or relay.
How it worksA visual walkthrough of the RFP → bid → accept → receipt lifecycle.
MarketplaceA simulated feed of open RFPs and bids.
Get startedStep-by-step commands for a requester or a provider.
AboutWhy this exists and where it's headed.