How it works

Four signed hops, one verifiable contract.

A requester wants a machine. A provider has one spare. Nothing changes hands until both sides have signed a record that says so — and anyone watching the firehose can check the whole chain later.

Step 1 · Requester

Publish the RFP

The requester signs a compute.vm record describing the machine — cpus, memory, disk, network, a role, and optional cloud-init user data — then wraps a strong reference to it in a market.rfp envelope. Both land on the requester's own PDS. The requester discovers bidders through the relay's offering index and vouch graph, then pushes the RFP to each bidder's submitRfp endpoint.

$type: market.rfp
payload:
  uri: at://did:plc:alice/compute.vm/3mm3dolfolz2c
  cid: bafyrei...vm
Step 2 · Providers

Bids arrive from the firehose

Bids arrive through two channels: direct submitBid XRPC calls (primary) and firehose discovery (fallback). Bidders create a market.bids.free payload (cost: 0 by default; x402 paid pricing is an alternative) and bind it to the RFP in a signed market.bid record.

$type: market.bid
rfp: {uri: at://did:plc:alice/market.rfp/3mm3d..., cid: bafyrei...rfp}
payload: {uri: at://did:plc:bob/market.bids.free/3mm4..., cid: bafyrei...free}
submitAccept: did:plc:bob#pdr_temp_market
Step 3 · Requester

Score bids, accept one

Once the window closes, the requester's policy filter and scorer — lowest cost, by default — pick a winner. Locking it in means signing a market.accept record that references both the RFP and the winning bid, then calling the provider's submitAccept endpoint.

$type: market.accept
rfp: {uri: at://did:plc:alice/market.rfp/3mm3d..., cid: bafyrei...rfp}
bid: {uri: at://did:plc:bob/market.bid/3mm4..., cid: bafyrei...bid}
submitEvent: did:plc:alice#pdr_temp_compute_event
Step 4 · Provider

Provision, settle, receipt

The provider mints a signed market.receipt immediately — the contract is settled before the VM finishes booting. Then cloud-init (tunnel-subscriber + sshd + ephemeral SSH key) provisions the guest. The receipt anchors rfp, bid and accept behind one content-addressed CID.

$type: market.receipt
rfp: {uri: ..., cid: bafyrei...rfp}
bid: {uri: ..., cid: bafyrei...bid}
accept: {uri: ..., cid: bafyrei...accept}
Step 5 · Requester

Verify receipt, SSH in

The requester verifies the receipt's signatures and proof CID binding (verifyRecordSignatures + verifyRemoteProof). Once valid, SSH connects through the relay tunnel: websocat ProxyCommand → dispatcher WebSocket → tunnel-subscriber on guest → sshd. The guest was bootstrapped with the requester's ephemeral SSH public key via cloud-init.

The contract's lifecycle
RFP open Bidding Scoring Accepted Settling Settled

If no bid passes the requester's policy, scoring ends in Rejected instead of Accepted — the RFP simply closes.

What the protocol enforces

See the full spec

Every record type, field and validation rule, laid out for reference.

Read the docs