Provide compute
You can sell sandbox time on your own machines through rent.dev: one command brings a node up, and every answer after that carries the next step, so a coding agent can finish the setup by following it. The request line below answers the full guide as Markdown, with no key.
https://api.rent.dev/ v1/ supply/ guideWhat a node is
A node is one machine, or one backend of yours, that runs the rent.dev node kit. It belongs to an ordinary rent.dev account, and an account can have many.
A node holds its own key and opens an outbound tunnel of its own, so it needs no open inbound port. It signs every request it sends, and it checks the signature on every request rent.dev sends it. Before a node takes paid work, rent.dev tests it: a conformance probe, then a real lease that rent.dev pays for.
There are two ways to bring one:
- A host: a Linux x86_64 machine with KVM and Docker. The installer runs inside the node kit image and sets up the sandbox stack for you, so the host needs Docker and nothing else. Pick this when you have hardware.
- A driver file: you already run sandboxes on a backend of your own. You write one small module that creates, lists, runs commands in and kills sandboxes, and the node kit serves it. Your machine needs Node 24, and Docker for the node's tunnel. Pick this when you have a backend but no spare host.
macOS, Windows, and Linux without KVM cannot be a host. With a backend of your own, the driver path works from any of them.
Bring a node up
The flow is the same on both paths:
- Check first. On a host,
rentdev-node checkruns the preflight checks. With a driver,up --mockruns the probe checks against it. Neither changes anything or needs an account. - Bring the node up with
rentdev-node up, giving a region label, your ask and how many sandboxes it runs at once. On a host it makes the host ready first, which takes a while on a first install. Then, with no account, it makes one. It enrols and claims the node, starts it and follows it until it can take leases, printing each state change. - If it stops early, run the
next.commandit printed, then the one after, until the action isnone. The loop lists every state and command.
The ask is your price per vCPU-hour and per GiB-hour of memory, in USD. The region is a generic label such as eu-central, never a city or a company.
On a host
# 1 Check the host. This changes nothing.
sudo docker run --rm -i --privileged --network host --pid host -v / :/ host \
-v / var/ run/ docker.sock:/ var/ run/ docker.sock <image> check
# 2 Bring the node up.
sudo docker run --rm -i --privileged --network host --pid host -v / :/ host \
-v / var/ run/ docker.sock:/ var/ run/ docker.sock <image> \
up --region eu-central --cpu-ask 0.02 --memory-ask 0.0025 --slots 8
# 3 Follow it until the action is none.
rentdev-node status --wait<image> is the node kit image, by digest; the full guide carries these lines with it filled in. A fresh host has no rentdev-node command, so the first two commands run it through the image. up writes /usr/local/bin/rentdev-node, a short script around the same docker run line, so from the third command on every rentdev-node command runs as it is written.
The installer is a privileged container that changes the host: it loads a firewall table, reserves hugepages and starts services. It names every change in its output, and a second run changes nothing that is already right. rentdev-node down undoes the changes and keeps the node's key, so a later up resumes the same node.
With a driver file
Write the driver first: one module whose default export is defineDriver from the rentdev-node package, given capacity, create, get, list, kill and exec. The full guide has the contract. A driver file imports rentdev-node without installing it: the copy that runs the command answers that import.
# 1 Test the driver: no account, no network, one line per check with its fix.
npx rentdev-node@<version> up --mock --driver <driver_file>
# 2 Bring the node up.
npx rentdev-node@<version> \
up --region eu-central --cpu-ask 0.02 --memory-ask 0.0025 --slots 8 \
--driver <driver_file>
# 3 Follow it until the action is none.
npx rentdev-node@<version> status --wait<version> is the node kit version, which the full guide names, and <driver_file> is the path of your driver. A driver machine has no rentdev-node command, so every command, and every next.command, runs through npx as the third command does.
The node itself is a background process that up starts, and it does not come back after a reboot. Run up again then, or keep rentdev-node serve --driver <driver_file> under your own process manager.
With a claim token
For a fleet, or when your agent already has an account, make the claim tokens through the API. POST /v1/supply/enrollments answers up to 1000 tokens, one per node, and both install lines. It takes an access token with the supply scope, which no API key carries; the REST reference has the body. Over MCP, rent_supply_enroll makes the same enrolment and answers the exact line for each token.
# On a host
sudo docker run --rm -i --privileged --network host --pid host -v / :/ host \
-v / var/ run/ docker.sock:/ var/ run/ docker.sock <image> \
up --claim-stdin <<< '<claim_token>'
# With a driver file
npx rentdev-node@<version> up --driver <driver_file> \
--claim-stdin <<< '<claim_token>'Run the command on each machine with its own token in place of <claim_token>. A token is shown once and works once, within 10 minutes. It arrives on stdin through the <<< here-string, never as an argument, so it never shows in a process list. The here-string needs bash or zsh.
At most 10 of an account's nodes may be linking at once. A claim past that ends with linking_limit and leaves its token unused: run the same line again after retry_after seconds. When the token would expire before a node leaves linking, next.action is re_enroll instead: make a new token after retry_after seconds and run the line with that.
On a host the claim comes after the install, and a first install can take longer than those 10 minutes. If the line ends with claim_invalid, make a new token and run the same line again. That run is quick, because the host is already set up.
A machine set up with a claim token holds no account key, so rentdev-node up --re-enroll stops there and makes no new account. When its node is revoked, make a new token the same way and run the same line again with --re-enroll added.
The loop
Every node view, and every Problem a supply route answers, carries next: { action, command, after_s }. The whole loop is: wait after_s seconds, run next.command word for word, and repeat until the action is none.
| State | What it means | Next action | Command |
|---|---|---|---|
linking | Claimed. The node is bringing up its tunnel, and rent.dev checks that it can reach the node. | wait | rentdev-node status --wait |
probing | rent.dev runs its conformance probe, then buys a first audit lease. | wait | rentdev-node status --wait |
probation | Passed. Requests that ask for all partner supply can be placed here. Audits go on. | none | Nothing to run. |
active | Promoted, after 7 passed audits in a row. Customer requests can be placed here by default; one that sets none is not. | none | Nothing to run. |
suspended | Takes no leases. Fix the cause, then run rentdev-node doctor. Once the checks pass, it mends the link if rent.dev cannot reach the node, and asks for a probe itself. Without the CLI, ask with POST /v1/supply/nodes/{id}:probe. | fix_checks | rentdev-node doctor |
revoked | Gone for good, with its link removed. Enrol the machine again to come back. | re_enroll | rentdev-node up --re-enroll |
The table shows the action each state asks for on its own. rent.dev overrides it with upgrade when the node agent is too old, and with fix_checks when a blocking check fails. Each action has one command:
| Action | Command | When |
|---|---|---|
start_link | rentdev-node up | The claim worked. Start the node. |
wait | rentdev-node status --wait | rent.dev is working. Wait after_s seconds and follow the node. |
fix_checks | rentdev-node doctor | A check failed. Read each fix, apply it, and run the checks again. |
upgrade | rentdev-node upgrade | The node agent is older than rent.dev accepts. |
re_enroll | rentdev-node up --re-enroll | The token or the node is no longer usable. Make a fresh enrolment. |
none | Nothing to run. | Nothing is left to do. A node on probation and an active node are both done. |
GET /v1/supply/nodes/{id} answers a node with its state, its checks, reachable and next. reachable says whether rent.dev reaches the node through its tunnel.
Checks
A check result is { code, status, source, detail, fix, doc_url }, and its status is one of pass, fail, warn, skip. Only a fail on a blocking check stops progress. Preflight checks run on the node; link and probe checks are run by rent.dev. rentdev-node doctor shows the node's last results, each with its fix.
| Code | Phase | Severity | Fix |
|---|---|---|---|
os_linux | preflight | blocking | Run the node on Linux (x86_64). |
kvm_available | preflight | blocking | Enable hardware virtualisation in the BIOS and load the kvm module so /dev/kvm exists. |
cgroup_v2 | preflight | blocking | Boot with the unified cgroup v2 hierarchy (systemd.unified_cgroup_hierarchy=1). |
docker_version | preflight | blocking | Install Docker Engine 28.3.3 or newer: older releases let LAN neighbours reach ports published on the loopback address (moby#45610, CVE-2025-54388). |
docker_compose | preflight | blocking | Install the Docker Compose v2 plugin. |
outbound_443 | preflight | blocking | Allow outbound TCP 443 to the rent.dev API from this host. |
outbound_7844 | preflight | blocking | Allow outbound TCP or UDP 7844 to the tunnel edge network: the tunnel needs it and 443 alone is not enough. |
disk_free_gib | preflight | blocking | Free disk space on the Docker data volume. |
memory_gib | preflight | blocking | Add memory, or stop other workloads: at least 24 GiB must stay available once sandbox hugepages are reserved. |
embed_ports_closed | preflight | blocking | Firewall the sandbox stack's host ports (3000-3003, 5007-5010, 5016-5018, 5109) and store ports (5432, 6379, 8123, 9000, 30006) from every non-loopback address. |
clock_skew | preflight | blocking | Enable NTP time sync (timedatectl set-ntp true): the node's gate refuses platform requests outside ±120 s, so this check fails above 60 s of skew. |
bare_metal | preflight | advisory | Prefer bare metal: nested KVM in a VM also passes /dev/kvm but runs sandboxes slower. |
no_unattended_upgrades | preflight | advisory | Disable automatic reboots from unattended upgrades: a reboot ends every lease on the node. |
other_schedulers | preflight | advisory | Kubernetes, Slurm or Nomad agents were found running; make sure they cannot place work on the cores and memory you offer. |
link_up | link | blocking | Start the node (rentdev-node up) and check that the tunnel container is running. |
gate_answers | link | blocking | Check that the gate listens on loopback port 3120 and loopback port 3122 (rentdev-node doctor). |
key_proof | link | blocking | The ping answer was not signed by this node's key: restore /etc/rentdev-node/node.json or re-enrol. |
template_base | probe | blocking | Build the standard templates so the alias base resolves (rentdev-node doctor). |
create | probe | blocking | The node could not start a 1 vCPU, 512 MiB sandbox; read the driver logs. |
exec | probe | blocking | A command inside the sandbox did not return the expected sha256 digest; check the sandbox runtime. |
shape_vcpu | probe | blocking | The sandbox saw fewer vCPUs than requested; do not cap sandbox CPUs below the request. |
shape_memory | probe | blocking | The sandbox saw under 90% of the requested memory; do not cap sandbox memory below the request. |
cpu_bench | probe | blocking | Per-vCPU throughput fell below a third of the control's; do not oversubscribe the host's cores. |
file_roundtrip | probe | blocking | A file written in the sandbox did not read back the same; check the sandbox filesystem. |
cold_start_ms | probe | advisory | Time to a ready sandbox, measured by the platform; keep templates cached to lower it. |
kill | probe | blocking | The sandbox did not stop when killed; read the driver logs. |
kill_idempotent | probe | blocking | A second kill must report the sandbox already gone, not fail or succeed again. |
no_orphans | probe | blocking | Sandboxes were left running after the probe; listing by lease metadata must return none. |
data_plane | probe | blocking | The sandbox was unreachable through its public hostname; check the proxy port 3122 and the tunnel. |
paid_path | probe | blocking | A paid lease did not start, run and settle on this node; read the driver logs for its sandbox. |
Problems
A refusal is an RFC 9457 Problem with a stable code, as everywhere on the API. On a supply route it also carries what an agent needs to act alone:
retryable: the same request may succeed if sent again later.retry_after: seconds to wait, when known. It is also theRetry-Afterheader.owner_action_required: only rent.dev can clear it. Retrying will not help until then.next: the step to run, as in the loop.checks: when a check failed, every result with its fix.
| Code | Status | Retry | Next action | What to do |
|---|---|---|---|---|
| claim_invalid | 410 | No | re_enroll | A claim token works once, within 10 minutes. When up made the enrolment, run rentdev-node up --re-enroll; when the token was made elsewhere, make a new one there and run the same line again with it. |
| claim_proof_invalid | 401 | No | re_enroll | The node key's signature over the claim token did not verify. Start again with a fresh enrolment. |
| checks_failed | 422 | No | fix_checks | checks lists every result with its fix. Fix each blocking fail, then run the same command again. The claim token stays unused until it expires. |
| node_exists | 409 | No | none | A live node already has this host's fingerprint, but this host's node file no longer names it. Revoke the old node (POST /v1/supply/nodes/{id}:revoke), then run the same command again. |
| supply_link_unavailable | 503 | Yes | wait | The node's tunnel could not be set up; detail names the limit when one was hit. Retry after retry_after seconds. When owner_action_required is true, rent.dev has to act first. |
| linking_limit | 429 | Yes | wait | At most 10 of an account's nodes may be linking at once, and this claim would be one more, so its token stays unused. When next.action is wait, retry after retry_after seconds, by when a node has linked or been removed; when it is re_enroll, the token expires first, so make a new one after retry_after seconds and claim with that. |
| not_found | 404 | No | none | No node has this id on your account. A node of another account answers the same. |
| node_revoked | 410 | No | re_enroll | The node is revoked for good. Enrol the machine again with rentdev-node up --re-enroll. A machine set up with a token made elsewhere needs a new token from there, on stdin, with --re-enroll added to the same line. |
| node_suspended | 403 | No | fix_checks | A suspended node may only send beats and events, relink and ask for a probe. Run rentdev-node doctor and apply each fix it reports. Once the checks pass, it mends the link if the platform cannot reach the node, and asks for the probe itself. |
| unauthenticated | 401 | No | fix_checks | The node's signature is missing, stale or wrong. Check the clock (clock_skew) and that the node file is intact. |
| node_replay | 409 | Yes | wait | This sequence number or nonce was already used. Sign the request again with a new one. |
| probe_in_progress | 409 | Yes | wait | A probe of this node is already running. Wait for it to finish; rentdev-node status --wait follows it. |
| payout_proof_required | 403 | No | none | Changing payout needs signed_nonce: a fresh nonce from GET /v1/accounts/nonce, and the account's agent key's signature over rent.dev payout v2\n{node_id}\n{nonce}\n{public_key}\n{network}\n{address}, with the address in lowercase and the network as the node will store them. |
| agent_upgrade_required | 400 | No | upgrade | The node agent is older than the platform accepts. Run rentdev-node upgrade. |
Each code links to its entry on the errors page. Supply routes also answer general codes, such as forbidden for a token without the supply scope.
Rates, the take and the holdback
You set an ask per vCPU-hour and per GiB-hour of memory. A customer pays your ask divided by one minus the take, and the take is 20%. Your share of each charge is the charge less the take, rounded down to the micro-dollar.
| Rate | Your ask | The customer pays |
|---|---|---|
| Per vCPU-hour | $0.02 | $0.025 |
| Per GiB-hour of memory | $0.0025 | $0.003125 |
Billing runs on rent.dev's clock. A lease is billed only for time your node signed for as running, in beats no more than 180 seconds apart, and that rent.dev saw alive. A longer gap is never billed, even if the sandbox comes back. Rates lock when a lease starts, so a new ask applies to new leases only.
Your share accrues when a lease ends. It becomes releasable 14 days later, or 120 days later when the paying account topped up by card in the 120 days before. That is how long a card payment can be disputed. Earnings freeze after a liveness mismatch on the node, or when the paying account disputes a card charge; frozen earnings are kept, never deleted.
Earnings accrue in your ledger, which GET /v1/supply/earnings answers.
Getting paid
Payouts are made in USDC. Each node is paid on its own, to the address registered on that node: once a day, one payout takes the node's releasable earnings. An hourly run makes the payouts, so a node that one run does not reach is paid by a later one.
A node with no registered address, or with one on another network than the payout network, is not paid: its earnings wait until an address is registered, and the earnings' unaddressed_micros shows that part. Holdbacks and freezes work as above: only releasable earnings that carry no freeze are paid.
# At enrolment: no proof is asked.
rentdev-node \
up --region eu-central --cpu-ask 0.02 --memory-ask 0.0025 --slots 8 \
--payout-address <0x-address>
# Later: signed with the account's agent key.
rentdev-node payout --address <0x-address>
# The address, the totals and the latest payouts.
rentdev-node payout<0x-address> is the address. rentdev-node payout --address takes a nonce, signs the proof for that address and network with the account's agent key, and changes the node; a machine set up with a claim token needs --agent-key <file> for it. Without the CLI, PATCH /v1/supply/nodes/{id} takes payout with signed_nonce, as the REST reference shows, and over MCP rent_supply_update_node does the same.
| State | What it means |
|---|---|
open | Its amount is fixed and the transfer is sent or about to be. Its earnings count as in_payout. |
paid | The finalized chain shows the transfer made. Its earnings count as paid_out. |
void | The finalized chain shows its last authorization expired unused, so nothing was sent. Its earnings are freed, and a later payout takes them. |
GET /v1/supply/payouts, or rent_supply_payouts over MCP, lists your payouts, newest first, or only one node's with node=<node id>. Each carries its nonce, its transaction once known, and verify: send it as an eth_call to any RPC of the network, and the token answers 1 once the payout landed.
A payout cannot be reversed, and once made it is not sent again to another address. Register the right address before the next payout takes the earnings.
Where customer traffic comes from
By default a customer's sandbox may run on an active partner node, scored like any other machine. A node on probation gets customer sandboxes only from requests that ask for it. A create says which partner nodes it allows with routing.partners:
none: never placed on a partner node: rent.dev's own machines and the customer's own vendor keys only.active: may be placed on active partner nodes. A request that leavesrouting.partnersout gets this.all: may be placed on active nodes and on nodes on probation.
On the E2B-compatible API a create has no routing member, so the same word goes in the sandbox's metadata, under the key rent.dev/partners.
So the time to your first customer lease depends on demand, and nothing here promises traffic. All partner nodes together share one exploration slice of 2% of placements, split evenly by account and then by node. Running many nodes under one account does not win more of it.
A node priced above rent.dev's own list rate for a sandbox is never explored. The node view's placement block shows your price against list, whether the node is explorable, and why not.
Audits
rent.dev buys leases from every node at unannounced times and pays for them like any customer: your own rate, capped at rent.dev's list rate. They arrive at random, at least 1 a day, or 10% of your customer leases per day over the last 7 days if that is more. They run ordinary developer work and check the results.
A failed check counts against a node only when the same check passed on rent.dev's own nodes at the same time, so an outage on rent.dev's side never suspends you.
What the checks prove
A pass shows that a node starts, sizes and ends sandboxes as it claims. Every answer comes through software the operator controls, so the checks catch bugs and outages and make cheating cost more than it pays. They are not cryptographic proof.
Whoever operates a node can read what runs on it, which is why a customer can keep any request off partner nodes with none.
The full guide
The full guide is one Markdown document the API serves with no key. It is built from the same tables as this page, with the node kit image and version filled in, and it adds the request bodies, the driver contract, every CLI option and environment variable, and how keys and signatures work.
curl -s https://api.rent.dev/ v1/ supply/ guide- The REST reference: every supply route, parameter and status.
- Errors: every Problem code and what to do about it.