Reference

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).

com.publicdomainrelay.temp.compute.vm

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}
com.publicdomainrelay.temp.market.rfp

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: [...]
com.publicdomainrelay.temp.market.bids.x402

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
market.bids.free

Free pricing payload

Zero-cost bid — the reference default. A reason string and signatures.

cost: 0
reason: "provided at no cost"
signatures: [...]
com.publicdomainrelay.temp.market.bid

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: [...]
com.publicdomainrelay.temp.market.accept

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: [...]
com.publicdomainrelay.temp.market.receipt

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.

VMRFPrfp.payload → vm
RFPBidBidsX402bid.rfp → rfp · bid.payload → x402
RFPAcceptBidaccept.rfp + accept.bid
ReceiptRFP+Bid+Accept

State machine

A contract moves through one path to Settled, or exits early to Rejected if scoring finds no bid it will accept.

StateEntered whenNext
RFP OpenRequester creates RFP + VMBidding
BiddingFirehose fans the RFP outScoring (window closes)
ScoringBid window closesAccepted or Rejected
RejectedNo bid passes policy— terminal
AcceptedRequester creates AcceptSettling
SettlingProvider provisions + collects paymentSettled
SettledProvider creates Receipt— terminal

Authority & validation

Rules a provider checks before it will settle a contract.

  • Accept.rfp and Bid.rfp strongRefs must be identical (uri AND cid). Enforced atomically by refsEqual. Error: ContractGraphError 'Accept.rfp does not match Bid.rfp' (HTTP 400).
  • The bid must carry a valid inline network.attested.signature by the bidder. Error: ContractGraphError 'Bid is missing a valid badge.blue signature'.
  • The RFP must carry a valid inline network.attested.signature by the requester. Error: ContractGraphError 'RFP is missing a valid badge.blue signature'.
  • The service-auth token issuer must be the record author (per submit handler). Error: 'service-auth token issuer must author the referenced record' (HTTP 403). For vm.delete: issuer must match market.accept author. Error: 'only the market.accept issuer may delete' (HTTP 403).
  • Receipt's proof CID must bind to the referenced accept record (verified by verifyRemoteProof).

Settlement

Two modes, both producing a signed market.receipt with the full contract chain and a proof CID binding accept → receipt.

Free settlement
  1. Requester creates accepts.free.
  2. Requester GETs /free/receipt/{acceptsFree.uri}/{acceptsFree.cid} (full AT-URI, not bare uri)
  3. Provider mints receipts.free with a proof CID.
  4. Verification checks NSID, author DID, CID binding.
X402 settlement
  1. Requester creates signed accepts.x402.
  2. Requester GETs the provider's receipt URL (constructed as {bidPayload.url}/{acceptsX402.uri}/{acceptsX402.cid})
  3. Provider validates payment, returns receipts.x402.
  4. Optional paymentMiddleware can 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.

EventPayload NSIDTrigger
vm.startedcom.publicdomainrelay.temp.compute.events.vm.startedVM / container boots
vm.onNetworkcom.publicdomainrelay.temp.compute.events.vm.onNetworkGuest gets an IP
vm.registerIdentitycom.publicdomainrelay.temp.compute.events.vm.registerIdentityGuest registers DID identity
vm.deletecom.publicdomainrelay.temp.compute.events.vm.deleteRequester 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