For operators

Run a worker.

Three lines in a file, and the machine you already run models on starts earning USDC. The worker sits next to your vLLM or ComfyUI, talks to it over HTTP, and takes care of the network. No sign-up, no approval, nothing to apply for.

Before you start

Three things. You probably have two already.

Docker

That's the whole install. The worker runs in one container and never touches your GPUs itself.

A model server

vLLM (or any OpenAI-compatible server) for text. ComfyUI for images. Either one, or both — whatever this box already runs.

A wallet address, to be paid

Any address on Base. A cold wallet is fine — nothing on the box ever holds your earnings. Skip it and the node still works; see serving for free.

Quickstart

Earning in five minutes.

Every box uses the same compose file. The only thing you write is .env, and it is three lines.

Get the code

terminal
$ git clone https://github.com/rootmodeai/rootmode
$ cd rootmode
$ cp .env.example .env

Fill in three lines

Where your model server is, what you charge, and where the money goes. That is the entire configuration.

.env
# 1. What this box serves (vLLM, SGLang, llama.cpp, … — or ComfyUI for images)
ROOTMODE_VLLM=http://127.0.0.1:8000

# 2. What it charges, in USD per million tokens (per image for ComfyUI)
ROOTMODE_VLLM_PRICE=0.15

# 3. Where your USDC lands. Any Base address.
ROOTMODE_PAYOUT=0xYourWalletAddress

You don't need to know a contract address, a chain id, an RPC or a bootstrap peer. All of that is built into the worker. Serving images instead? Swap the first two lines for ROOTMODE_COMFYUI and ROOTMODE_COMFYUI_PRICE.

Start it

terminal
$ docker compose up -d --build
$ docker compose logs -f

It tells you what it is serving, and where it settles:

docker compose logs -f
INFO settle signer 0x1fdc1df454aa7ef9433ec00e6798a3b1e5cf0ba2
INFO rootmode worker v0.1.10
INFO listening on ws://0.0.0.0:9944
INFO settling on pot 0x8115…D5a9 (chain 8453) via https://mainnet.base.org — payout 0xYour…ress
INFO caps: [llm]  max_concurrent: 2
INFO   meta-llama/Llama-3.1-8B-Instruct (llm)

Send the gas key a few dollars of ETH

Every paid job ends with your node posting a tiny transaction on Base to collect — about a tenth of a cent each. It signs those with its own key, minted on first start and printed as settle signer above. That key needs a little ETH, once:

do this once
Send 0.002 ETH on Base to the settle signer address from the log.
# ≈ a few dollars, ≈ 3,500 collections. Gas only — never your revenue.

Until the ETH arrives the node refuses to charge (it says so in the log, and keeps retrying on its own). Once it lands, you're live. It warns you in the log when the key runs low.

That's it

Clients on the network find your node and pick it by price. Each job locks the client's money before your GPU runs, bills what was actually used, and settles on Base: 90% to your payout address, 10% to the network. Nothing about the job — no prompt, no answer — goes on-chain.

Keep the Docker volume.

/var/lib/rootmode holds your node's identity, its gas key, and bills it hasn't collected yet. Never docker compose down -v. Losing the volume loses money, not just a name.

Or

Serve for free.

Leave the price out. A free node needs no wallet, no ETH, and never touches the chain — clients just pick it as the cheapest option. Add a price later and it starts charging on the next restart.

.env
ROOTMODE_VLLM=http://127.0.0.1:8000
# no price, no payout: free, and off-chain

Questions

The ones people actually ask.

Do I need crypto to start?

To serve for free, no. To charge, you need one wallet address on Base to receive USDC (any wallet will do), and about $5 of ETH on Base sent to the gas key your node prints. That ETH covers thousands of jobs.

Where does the money actually go?

90% of every settled job goes to ROOTMODE_PAYOUT as USDC on Base, 10% to the network's treasury. The node's own key only ever holds gas. If you don't set a payout, earnings accrue to that gas key on the volume — the log tells you the address on every start — so set one before it adds up.

What if a client doesn't pay?

They can't not pay. A client's funds are locked in the settlement contract before your GPU does anything, and your node checks that lock first. If the money isn't there, the job is refused before it runs.

How do clients find me?

Your node announces itself to the network on start, through bootstrap peers built into the worker. Clients see what you serve and your price, and choose. On the same LAN the desktop app finds you without any of that.

Can I price models differently?

Yes: ROOTMODE_VLLM_PRICES=model-a=0.40,model-b=0.10 overrides the flat price per model. Same for ComfyUI with ROOTMODE_COMFYUI_PRICES.

I'm behind NAT / on Docker Desktop.

Linux with host networking needs nothing. Otherwise set ROOTMODE_P2P_EXTERNAL to the address other peers should dial (/ip4/203.0.113.10/tcp/4101), or peers will find you and then fail to reach you.

How do I stop?

docker compose down. You're off the network instantly; there's no notice period and nothing to cancel. Just don't add -v.

What if I lose the volume?

Your peer id changes (clients that pinned you stop recognising the node), the gas key and its ETH are gone, and any bills not yet collected are lost. Back it up like you'd back up a wallet.

Configuration

Every setting, in three groups.

All go in .env. The first group is the quickstart; the other two you may never touch.

The three lines

VariableExampleWhat it does
ROOTMODE_VLLMhttp://127.0.0.1:8000Your text model server. Comma-separate several.
ROOTMODE_COMFYUIhttp://127.0.0.1:8188Your ComfyUI, for images. Every installed checkpoint is served.
ROOTMODE_VLLM_PRICE0.15USD per million tokens, every model. Unset = free.
ROOTMODE_COMFYUI_PRICE0.02USD per image, every checkpoint. Unset = free.
ROOTMODE_PAYOUT0x…Where your 90% USDC lands. Unset = the node's own gas key, until you name one.

Optional

VariableDefaultWhat it does
ROOTMODE_VLLM_PRICES—Per-model prices: id=0.40,id=0.10. Win over the flat price.
ROOTMODE_COMFYUI_PRICES—Per-checkpoint prices: sdxl=0.02,krea2-turbo=0.08
ROOTMODE_LABELhostnameThe name clients see
ROOTMODE_COUNTRY—Two-letter country code, shown beside the label
ROOTMODE_MAX_CONCURRENT2Jobs at once
ROOTMODE_PAY_KEYmintedBring your own gas key (32-byte hex) instead of the one minted on the volume
ROOTMODE_P2P_EXTERNAL—Address to advertise when behind NAT or published ports
ROOTMODE_REQUIRE_SIGNATUREfalseRefuse unsigned submissions
ROOTMODE_ALLOW_PEERS—Only these client peer ids may submit
ROOTMODE_VLLM_API_KEY—If your model server wants one
ROOTMODE_COMFYUI_WORKFLOWbuilt-in graphYour own ComfyUI workflow (API format) for pipelines with a shape of their own

Built in

Compiled into the worker from the network's deploy record, so you never copy them. Shown so nothing is hidden; set one only to point at a different deployment.

VariableBuilt-in valueWhat it is
ROOTMODE_POT0x811544168635F23C2F31aD5d9aAD874Bd8A4D5a9The settlement contract on Base
ROOTMODE_CHAIN_ID8453Base
ROOTMODE_RPChttps://mainnet.base.orgFails over to other public Base RPCs on its own
ROOTMODE_BOOTSTRAPbootstrap.rootmode.aiThe network's entry points

Prefer a file? Mount your own worker.toml and set ROOTMODE_CONFIG to its path — then none of the above apply. The full reference is in docs/WORKER.md.

Your machine, your rules

What a client can and can't do to your box.

Nothing a client sends is executed A job picks a model you offer and sends words. No job field reaches a process spawn.
You choose what's on offer The worker advertises what your server actually serves, and nothing else can be requested.
You're paid before the GPU runs The client's funds are locked first. No lock, no job.
You can restrict who submits at all Require signed submissions, or list the exact client peer ids allowed.
Stop means stop docker compose down and you're off the network. No notice period, nothing to cancel.
You're responsible for what your node serves.

Workers run models the operator chose, on hardware the operator owns. Local law and acceptable use are yours to observe.

Going further

When one node becomes several.

Nodes find each other through the network's bootstrap peers, which are built in. Running your own is optional: it's an entry point and a relay for NAT'd nodes — never a registry, never in the path of a job.