Join our community of builders on Discord!

Run a Worker on Mainnet

Workers are the supply side of the Lightchain AI network. A worker runs whitelisted models via Ollama alongside the Worker Sidecar (shipped as a Docker image), serves encrypted inference jobs through the Worker Gateway, and earns fees for every completed job. This guide walks through the end-to-end lifecycle on Mainnet — from generating a worker key and funding it to registering on-chain, going live, and deregistering when you're done. Mainnet is still dispatcher-routed: a dispatcher selects your worker and delivers jobs over the Worker Gateway WebSocket, which is what the commands below configure. The network is switching to dispatcher-free assignment — workers claim sessions on-chain — in the week of 2026-09-14. Read Prepare for dispatcher-free mode before then.
Mainnet LCAI is real value. Double-check every address, RPC URL, and chain ID before signing transactions. Funds sent to the wrong address are unrecoverable.

Prerequisites

  • Docker installed
  • GPU machine with enough VRAM for your model (8 GB+ for llama3-8b, 40 GB+ for llama3-70b)
  • Ollama installed and running
  • Funder wallet holding at least 50,001 LCAI (50,000 stake + ~1 gas) — this is your existing wallet that pays to bring the worker online. It is NOT the worker key. You will generate a fresh worker key in Step 0 and send LCAI from the funder to it in Step 5.
  • Foundry installed (cast is used for key generation, balance checks, and contract reads)

Network reference

ServiceValue
RPC URLhttps://rpc.mainnet.lightchain.ai
Chain ID9200
Beacon APIhttps://beacon.mainnet.lightchain.ai
Worker Gatewayhttps://worker-gateway.mainnet.lightchain.ai

Resolve mainnet contract addresses

AIConfig and JobRegistry are deployed at runtime, so their addresses are best resolved from the predeployed WorkerRegistry rather than hard-coded. Resolve them once and export them — the docker run steps below pick them up from your shell:
CodeBASH
Cross-check the values against the Mainnet Contract Addresses page before continuing.

Step 0: Generate a fresh worker key

Always use a dedicated, brand-new key for the worker. Do not reuse your funder key — the worker key sits in a Docker container with the keystore password, and should hold only the working capital (stake + small gas buffer) you're comfortable exposing to that machine.
CodeBASH
Copy the printed Address and Private key, then export them:
CodeBASH
Sanity check — the privkey must derive to the address you'll be funding and registering:
CodeBASH
The output must equal $WORKER_ADDR. If it doesn't, stop and re-export — anything sent to the wrong address is unrecoverable, and registering with mismatched keys locks the stake on the wrong account.
Do not set WORKER_PRIVKEY to your funder's private key. The two must be different. The funder pays from your existing wallet (Step 5); the worker is the new key from this step.

Step 1: Install Ollama and pull the model

On-chain model names are the Ollama tags, used verbatim (the on-chain modelId is keccak256(name)). Pick from Whitelisted models and pull each one into Ollama. Only llama3-8b and llama3-70b are not native Ollama tags — create them as aliases. The example below uses llama3-8b.
CodeBASH

Step 2: Pull the worker image

CodeBASH

Step 3: Import the worker private key

Imports $WORKER_PRIVKEY (set in Step 0) into an encrypted keystore on disk:
CodeBASH
The Address: printed by the command must equal $WORKER_ADDR from Step 0. If they differ, stop — the wrong key was imported.

Step 4: Generate the ECDH encryption key

Every worker advertises an encryption public key on-chain so users can encrypt prompts for it — keygen produces that pair locally.
CodeBASH
The encryption private key stays on the machine running inference — it is what decrypts incoming prompts.

Step 5: Fund the worker from your funder wallet

Send LCAI from your funder wallet (the existing one mentioned in Prerequisites) to $WORKER_ADDR (the fresh address from Step 0). The worker needs at least 50,001 LCAI: 50,000 for the minimum stake plus ~1 for gas (register TX + per-job ack and complete TXs over its lifetime). 50,005 LCAI gives a comfortable buffer. If your funder wallet is in MetaMask/Rabby, send the transfer through the wallet UI. If you have its private key locally and want to use Foundry:
CodeBASH
Verify the worker received the funds:
CodeBASH
Common mistake: setting FUNDER_PRIVKEY to the same value as WORKER_PRIVKEY. That sends LCAI from the worker to itself (a no-op minus gas) and the worker stays unfunded. Cross-check with cast wallet address --private-key "$FUNDER_PRIVKEY" — it must NOT equal $WORKER_ADDR.

Step 6: Register on-chain

CodeBASH
This will:
  • Stake 50,000 LCAI (auto-queried from AIConfig)
  • Register your ECDH public key on-chain
  • Add llama3-8b to your supported models
You can only serve models that are currently whitelisted on AIConfig. Attempting to register for a delisted or non-existent model will revert.

Step 7: Run the worker

The --add-host flag below makes host.docker.internal resolve to the Docker host on Linux (where it isn't provided by default). On macOS and Windows it's a no-op — Docker Desktop already maps that hostname — so the same command works everywhere.
CodeBASH
RELEASE_STATE_PATH keeps the release scheduler's settlement ledger on the mounted volume, so a docker rm can no longer orphan claimable earnings (see Drain & Graceful Exit).

Step 8: Verify it's working

CodeBASH
The worker emits structured JSON logs. A healthy startup shows roughly this sequence (timestamps and addresses elided for brevity):
CodeTEXT
If you see all of those — particularly worker registration validated, authenticated with worker-gateway, and websocket connected to gateway — the worker is fully online. Grep for the stable substrings if you don't want to read the full output:
CodeBASH
The worker is now:
  • Sending heartbeats every 10s via the gateway
  • Connected via WebSocket for instant job delivery
  • Ready to receive and process inference jobs
When a job lands, you'll see a multi-stage log sequence (ws_job_received → stage 1 complete → stage 2 starting → … → job completed). Tail with:
CodeBASH

Whitelisted models

All of these are registered on AIConfig and whitelisted on WorkerRegistry (values as of 2026-09-13; read the live fee with cast call $AI_CONFIG_ADDRESS "getModelFee(bytes32)(uint256)" $(cast keccak <name>) --rpc-url $RPC_URL):
Model (on-chain name = Ollama tag)Fee per jobMax output tokens
llama3-8b0.02 LCAI2,048
llama3-70b0.15 LCAI4,096
gemma4:e2b0.02 LCAI2,048
glm-4.7-flash0.02 LCAI8,192
qwen3-coder-next0.05 LCAI16,384
gpt-oss:20b0.04 LCAI8,192
gpt-oss:120b0.20 LCAI8,192
qwen3-vl:8b0.02 LCAI4,096
qwen3-vl:30b0.08 LCAI4,096
qwen3-embedding:0.6b0.005 LCAI1
tts-piper0.02 LCAI2,048
To serve several models, list them comma-separated in SUPPORTED_MODELS (for example SUPPORTED_MODELS=llama3-8b,gemma4:e2b) in Steps 4, 6 and 7, pull each into Ollama in Step 1, and re-run register (or add-models for a worker that is already registered). Every name must match the on-chain name exactly — a different string hashes to a different modelId and the registration reverts with ModelNotWhitelisted.

Rewards and fund handling

Per-job fees earned by the worker are paid out directly to the worker's wallet ($WORKER_ADDR from Step 0) as jobs complete. There is no separate payout address registered on-chain and no automatic forwarding to the funder wallet — earnings simply accumulate on the worker key. Because the worker key lives inside the Docker container alongside its keystore password, you should not treat it as long-term storage. Sweep accumulated fees to your own designated wallet (typically the funder, a hardware wallet, or any cold-storage address you control) on whatever cadence matches your risk tolerance. Check the worker's current balance:
CodeBASH
Sweep funds from the worker to your designated wallet, leaving a small gas buffer behind so the sidecar can keep paying for JobAcknowledged / JobCompleted transactions:
CodeBASH
Do not drain the worker wallet to zero while the worker is registered and running — it needs gas to ack and complete future jobs. Missed deadlines lead to slashing (see Slashing & Rehabilitation). Leaving ~1 LCAI behind is usually enough; top up from your funder if it dips.
The staked 50,000 LCAI is held by WorkerRegistry, not by the worker wallet, and is only released when you deregister (see Deregister and withdraw stake).

Check registration status

CodeBASH

Prepare for dispatcher-free mode

When mainnet flips AIConfig.setSortitionEnabled(true), the dispatcher stops assigning work: consumers request sessions on SessionManager and workers claim them on-chain. A worker still running the commands above would keep its gateway connection but never receive another job. What changes for you:
  1. Re-pull the image. registry.lightchain.ai/mainnet/worker:latest (build f24e37e, 2026-09-13) supports sortition, preflight and web search. Builds pulled before that date (tag f7a959c, May 2026) ignore the variables below, so docker pull again before changing anything.
  2. Four environment variables change in the Step 7 command (everything else stays):
    CodeBASH
    With SORTITION_ENABLED and WORKER_GATEWAY_URL both set the worker runs the external profile — it claims on-chain and streams answers through the gateway, with no Redis. worker-gateway-v2 is the gateway build that accepts those streams; it runs beside the current gateway until the switch, after which the two hostnames point at the same service.
  3. Gas matters more. The worker pays for its own claimSession transactions, so keep more than the ~1 LCAI buffer the dispatcher flow needed (10 LCAI is comfortable).
  4. drain no longer takes you out of rotation — stop the container with docker stop -t 90 instead. See Stopping a dispatcher-free worker.
Never point the external profile at worker-gateway.mainnet.lightchain.ai (the current gateway): it rejects streamed responses, so jobs would settle on-chain while users see no answer. Use worker-gateway-v2 as above, and wait for the operators-channel go-ahead before switching — the two paths are being verified side by side.
The full mode description, environment reference and web-search setup are on Dispatcher-free Mode. Testnet already runs this way, so Run a Worker on Testnet is the rehearsal.

Slashing and suspension

Workers that miss deadlines or lose disputes get their stake slashed. After three offenses the worker is automatically suspended for 7 days and must call WorkerRegistry.reinstate() to come back online. During the September 2026 transition mainnet's slash rates are set to 0 and automatic suspension is switched off, but offences are still recorded. Full mechanics, live rates, and the step-by-step recovery runbook are in Slashing & Rehabilitation.

Drain before exit

Don't just docker stop a registered worker — jobs it already accepted carry an on-chain dispute window, and deregister reverts with ActiveJobsExist until that window plus a release cycle has passed. The drain → wait → deregister flow, including the fast-exit shortcut, recovery scenarios, and the cast calls used to poll activeJobsCount, is in Drain & Graceful Exit.

Deregister and withdraw stake

Run this only after draining and confirming activeJobsCount is 0 — otherwise it reverts with ActiveJobsExist. See Drain & Graceful Exit for the full flow.
CodeBASH
This removes your worker from the registry and returns your staked LCAI (minus any slashing penalties).

Stop the worker

Only safe after deregister succeeded — see Drain & Graceful Exit for why stopping earlier freezes activeJobsCount and can orphan claimable earnings.
CodeBASH

Mainnet contract addresses

ContractAddress
WorkerRegistry0x0000000000000000000000000000000000001002 (predeploy)
AIConfigResolve from WorkerRegistry — see Resolve mainnet contract addresses
JobRegistryResolve from WorkerRegistry — see Resolve mainnet contract addresses
SessionManager0xfFFCed75eF4Cdae0Cc33A27Bb46531B957a9f4e7 — or cast call $AI_CONFIG_ADDRESS "getSessionManagerAddress()(address)" --rpc-url $RPC_URL
FeePool0x0000000000000000000000000000000000001004 (predeploy)
Full address book on the Mainnet Contract Addresses page.