Lightchain worker on your own hardware — Windows, macOS, or Linux — with every gotcha we hit in production already fixed.
This repo is the PowerShell + Bash automation I wish existed when I onboarded my first Lightchain worker. The official docs at workers-testnet.lightchain.ai/run-node are correct, but they assume a bash shell, leave several real-world failure modes undocumented, and require a lot of copy-paste-edit-pray. This toolkit replaces all of that with idempotent scripts, layered docs, and a battle-tested troubleshooting guide.
- What you get
- Who this is for
- Status
- Quick start (TL;DR)
- Repository layout
- The 9 phases at a glance
- Costs and economics
- Hardware requirements
- Network reference
- Detailed guides
- The gotchas this toolkit fixes for you
- Operations cheatsheet
- Security model in one paragraph
- Contributing
- License
- Acknowledgements
| Layer | What's in this repo |
|---|---|
| Scripts | 16 PowerShell scripts + 16 Bash scripts that automate every phase of the official onboarding (key gen → register → run) and the day-2 ops (status, sweep, deregister, tail). |
| Docs | 9 guides covering architecture, per-OS installation, deep dives into each phase, day-2 operations, troubleshooting, security, and FAQ. |
| Examples | systemd unit for unattended Linux operation, docker-compose alternative, full env-var reference. |
| Repo hygiene | MIT license, CONTRIBUTING guide, SECURITY policy, CHANGELOG, .gitignore that actually protects your keys, .gitattributes for cross-OS line endings, .editorconfig, GitHub Actions for PowerShell + Bash + Markdown linting, issue/PR templates. |
Everything in scripts/ is idempotent: each phase script can be safely re-run, and refuses to do destructive things (overwrite keystore, send LCAI to wrong address) without explicit confirmation.
- Solo operators who want to run one or two workers on a home machine and earn $LCAI for serving llama3-8b inference jobs.
- Operators on Windows who don't want to install WSL2 just to run Linux-shell commands.
- Operators on Linux/macOS who want a clean, scripted alternative to the copy-paste docker run lines.
- Anyone who's already hit one of the gotchas in
docs/troubleshooting.mdand wants to skip the hours of debugging.
Not for: people building inference clients (you want the dispatcher API), people running validators / chain nodes (different stack, different image, separate access), or people who want managed hosting (try a cloud provider).
| Capability | Status |
|---|---|
| Mainnet onboarding (Windows) | ✅ Verified end-to-end on RTX 5060 Ti + Windows 11. |
| Mainnet onboarding (Linux) | ✅ Verified on Ubuntu 24.04 + RTX 4070. |
| Mainnet onboarding (macOS) | ✅ Verified on M2 Pro 16GB. |
| Testnet onboarding | ✅ Same scripts, flip NETWORK=testnet. |
| Multi-worker on one host | ✅ Documented in operations.md. |
| Job processing | ✅ Confirmed (stage 1 → 8 → job completed). |
| Sweep / deregister | ✅ Working. |
| Auto-restart on host reboot | ✅ Container has --restart always; systemd example included. |
You'll need: Docker Desktop, Ollama, Foundry's cast, an 8+ GB GPU, and 50,005 LCAI in a funder wallet (for mainnet).
# One-time install (run as admin)
winget install --id Docker.DockerDesktop --exact -e --silent
winget install --id Ollama.Ollama --exact -e --silent
# Foundry: see docs/installation-windows.md (~30 seconds)
# Clone this toolkit
git clone https://github.com/lightchain/lightchain-worker-toolkit.git
cd lightchain-worker-toolkit
# Onboard
.\scripts\powershell\00-generate-key.ps1
.\scripts\powershell\01-resolve-addresses.ps1
.\scripts\powershell\02-prepare-ollama.ps1
.\scripts\powershell\03-pull-image.ps1
.\scripts\powershell\04-import-key.ps1
.\scripts\powershell\05-generate-ecdh.ps1
$env:FUNDER_PRIVKEY = "0xYOUR_FUNDER_KEY"
.\scripts\powershell\06-fund-worker.ps1 # type 'send'
.\scripts\powershell\07-register.ps1 # type 'register' - 50k LCAI staked
.\scripts\powershell\08-run-worker.ps1 -NoTail
# You're live. Watch jobs flow in:
.\scripts\powershell\tail-jobs.ps1macOS: Apple ships bash 3.2 (from 2007). Two of the scripts use bash 4+ syntax and will error with
bad substitution. Install a modern bash before running the toolkit:brew install bash && echo 'export PATH="/opt/homebrew/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc. See docs/installation-macos.md for details.
# One-time install (Ubuntu/Debian)
sudo apt-get install -y docker.io
curl -fsSL https://ollama.com/install.sh | sh
curl -L https://foundry.paradigm.xyz | bash && ~/.foundry/bin/foundryup
# Clone this toolkit
git clone https://github.com/lightchain/lightchain-worker-toolkit.git
cd lightchain-worker-toolkit
# Onboard
./scripts/bash/00-generate-key.sh
./scripts/bash/01-resolve-addresses.sh
./scripts/bash/02-prepare-ollama.sh
./scripts/bash/03-pull-image.sh
./scripts/bash/04-import-key.sh
./scripts/bash/05-generate-ecdh.sh
export FUNDER_PRIVKEY=0xYOUR_FUNDER_KEY
./scripts/bash/06-fund-worker.sh # type 'send'
./scripts/bash/07-register.sh # type 'register' - 50k LCAI staked
./scripts/bash/08-run-worker.sh --no-tail
# You're live. Watch jobs flow in:
./scripts/bash/tail-jobs.shWant to play it safe first? Set $env:NETWORK = "testnet" (PowerShell) or export NETWORK=testnet (Bash) before Phase 01, claim free LCAI from the faucet, and run the whole flow with zero financial risk.
.
├── README.md # ← you are here
├── LICENSE # MIT
├── CONTRIBUTING.md # how to contribute
├── SECURITY.md # vulnerability disclosure
├── CHANGELOG.md # version history
├── .gitignore # protects keys + secrets
├── .gitattributes # cross-OS line endings (LF for .sh, CRLF for .ps1)
├── .editorconfig # editor defaults
│
├── docs/ # 9 layered guides
│ ├── architecture.md # how the worker fits into Lightchain (sequence diagram included)
│ ├── installation-windows.md # Windows install (winget + manual Foundry)
│ ├── installation-linux.md # Linux install (apt + native Ollama + Foundry)
│ ├── installation-macos.md # macOS install (Homebrew + Metal)
│ ├── onboarding.md # what each phase does, with verification steps
│ ├── operations.md # day-2: monitoring, sweeping, deregister
│ ├── troubleshooting.md # 16 named failure modes with fixes
│ ├── security.md # threat model + hardening recommendations
│ └── faq.md # common questions
│
├── scripts/
│ ├── powershell/ # Windows PowerShell scripts
│ │ ├── env.ps1 # network selection + shared config
│ │ ├── secrets.example.ps1 # secrets template (copy to secrets.ps1)
│ │ ├── common.ps1 # shared helpers (Invoke-Worker, etc.)
│ │ ├── 00-generate-key.ps1
│ │ ├── 01-resolve-addresses.ps1
│ │ ├── 02-prepare-ollama.ps1
│ │ ├── 03-pull-image.ps1
│ │ ├── 04-import-key.ps1
│ │ ├── 05-generate-ecdh.ps1
│ │ ├── 06-fund-worker.ps1
│ │ ├── 07-register.ps1
│ │ ├── 08-run-worker.ps1
│ │ ├── status.ps1
│ │ ├── stop.ps1
│ │ ├── tail-jobs.ps1
│ │ ├── deregister.ps1
│ │ └── sweep-rewards.ps1
│ │
│ └── bash/ # Linux/macOS Bash scripts
│ ├── env.sh
│ ├── secrets.example.sh
│ ├── common.sh
│ ├── 00-generate-key.sh
│ ├── ... (mirrors powershell/)
│ └── sweep-rewards.sh
│
├── examples/
│ ├── env.example # every env var documented
│ ├── docker-compose.yml # compose alternative to 08-run-worker.{ps1,sh}
│ └── systemd/
│ └── lightchain-worker.service # systemd unit for unattended Linux operation
│
└── .github/
├── workflows/
│ └── lint.yml # PSScriptAnalyzer + shellcheck + markdownlint
├── ISSUE_TEMPLATE/
│ ├── bug_report.yml
│ ├── feature_request.yml
│ └── config.yml # Discord links etc.
└── PULL_REQUEST_TEMPLATE.md
flowchart LR
P0[00 Generate<br/>worker key]
P1[01 Resolve<br/>addresses]
P2[02 Prepare<br/>Ollama]
P3[03 Pull<br/>image]
P4[04 Import<br/>key]
P5[05 Generate<br/>ECDH]
P6[06 Fund<br/>worker]
P7[07 Register<br/>50k stake]
P8[08 Run<br/>sidecar]
P0 --> P1 --> P2 --> P3 --> P4 --> P5 --> P6 --> P7 --> P8
P0 -.->|writes| S[(secrets.ps1)]
P1 -.->|writes| R[(resolved.ps1)]
P4 -.->|writes| K[~/lightchain-worker/keys/eth-keystore/]
P5 -.->|writes| E[~/lightchain-worker/keys/worker-encryption.key]
P7 -.->|on-chain| C[WorkerRegistry contract]
P8 -.->|long-running| D[Docker container]
| # | Phase | What changes | Reversible? | Time |
|---|---|---|---|---|
| 00 | Generate worker key | New secp256k1 keypair, stored in secrets.ps1 |
Yes (delete the file, generate a fresh one) | ~1 sec |
| 01 | Resolve addresses | Reads 2 on-chain proxy addresses, persists locally | Yes (just re-run) | ~1 sec |
| 02 | Prepare Ollama | Pulls llama3:8b if missing, creates llama3-8b alias |
Yes (ollama rm llama3-8b) |
30s-5min |
| 03 | Pull image | Downloads ~95 MB worker Docker image | Yes (docker rmi) |
30s-2min |
| 04 | Import key | Writes encrypted keystore file to disk | Yes (delete file, re-run) | ~5 sec |
| 05 | Generate ECDH | Writes worker-encryption.key to disk |
Yes BUT requires re-register if already registered | ~5 sec |
| 06 | Fund worker | On-chain tx: sends 50,005 LCAI funder → worker | Yes (sweep back) | ~10 sec |
| 07 | Register | On-chain tx: stakes 50,000 LCAI | Yes (deregister returns the stake minus any slashing) |
~10 sec |
| 08 | Run sidecar | Starts long-running container | Yes (stop.ps1 removes it; registration + stake stay on-chain) |
~3 sec |
| Item | Amount |
|---|---|
| Stake (locked in WorkerRegistry) | 50,000 LCAI |
| Funding tx gas | < 0.01 LCAI |
| Registration tx gas | < 0.05 LCAI |
| Gas buffer in worker wallet | ~5 LCAI (recommended) |
| Total to fund | ~50,005 LCAI |
| Item | Amount |
|---|---|
Gas for ackJob |
< 0.0005 LCAI |
Gas for completeJob |
< 0.0005 LCAI |
| Reward | varies by AIConfig |
| Item | Amount returned |
|---|---|
| Stake | 50,000 LCAI (minus slashing) |
| Worker wallet balance at deregister | Sweep with sweep-rewards |
Testnet is identical in flow but free — claim LCAI from lightfaucet.ai instead of using real money.
| Spec | Minimum | Recommended |
|---|---|---|
| CPU | 4 cores x86_64 or Apple Silicon | 8+ cores |
| RAM | 16 GB | 32 GB+ |
| Storage | 50 GB free | 100 GB+ NVMe |
| GPU | 8 GB VRAM (CUDA or Metal) | 16-24 GB VRAM |
| Network | 100 Mbps symmetric | 1 Gbps |
GPUs confirmed to work for llama3-8b (Q4 quantization, ~4.7 GB on disk, ~5-6 GB VRAM in use):
- NVIDIA RTX 3060 / 3060 Ti / 3070 / 3080 / 3090, RTX 4060 Ti / 4070 / 4080 / 4090, RTX 5060 Ti, A4000 / A5000 / A6000 / L4 / A10 / A100.
- Apple Silicon M1 Pro / M2 / M2 Pro / M3 / M3 Max / M4 / M4 Max (Metal acceleration is automatic).
- AMD Radeon RX 7900 XT / XTX via ROCm (untested by us, reportedly works with Ollama 0.5+).
Cards with < 8 GB VRAM will OOM loading the model. CPU-only inference is too slow to be competitive.
| Resource | Mainnet | Testnet (verify on the run-node site) |
|---|---|---|
| Chain ID | 9200 |
8200 |
| Chain RPC | https://rpc.mainnet.lightchain.ai |
https://rpc.testnet.lightchain.ai |
| Beacon API | https://beacon.mainnet.lightchain.ai |
https://beacon.testnet.lightchain.ai |
| Worker Gateway | https://worker-gateway.mainnet.lightchain.ai |
https://worker-gateway.testnet.lightchain.ai |
| Dispatcher | https://dispatcher.mainnet.lightchain.ai |
- |
| Status page | https://status.mainnet.lightchain.ai |
- |
| Worker image | us-central1-docker.pkg.dev/lightchain/lightchain-mainnet-public-docker/worker:latest |
us-central1-docker.pkg.dev/lightchain/lightchain-testnet-public-docker/worker:latest |
WorkerRegistry |
0x0000000000000000000000000000000000001002 (genesis predeploy, same on both) |
same |
AIConfig proxy |
0x24D11533C354092ed6E18b964257819cE78Ce77D |
resolve via cast call $WORKER_REGISTRY_ADDRESS "aiConfig()(address)" |
JobRegistry proxy |
0xfB15F90298e4CcD7106E76fFB5e520315cC42B0b |
resolve via cast call $WORKER_REGISTRY_ADDRESS "jobRegistry()(address)" |
AIConfig and JobRegistry are upgradeable proxies; the toolkit resolves them at runtime via Phase 01, so they auto-track any protocol upgrade.
| Guide | When to read |
|---|---|
docs/architecture.md |
Want to understand the 8 job-processing stages or the metrics endpoint. |
docs/installation-windows.md |
Fresh Windows machine, need Docker + Ollama + Foundry. |
docs/installation-linux.md |
Fresh Linux machine. |
docs/installation-macos.md |
Fresh macOS machine (Apple Silicon or Intel). |
docs/onboarding.md |
Want to know what each phase actually does and how to verify. |
docs/operations.md |
You're past onboarding and want day-2 ops (monitor / sweep / deregister). |
docs/troubleshooting.md |
Something is broken. Always check here first. |
docs/security.md |
Want to understand the threat model + key management hardening. |
docs/faq.md |
"How much can I earn?" / "Can I run this on a VPS?" / etc. |
These are real failure modes we hit while running a mainnet worker. The toolkit defaults around them; the troubleshooting guide explains each in depth.
The worker computes keccak256(SUPPORTED_MODELS) locally and uses that hash to look up incoming jobs. The gateway sends jobs keyed by keccak256("llama3-8b") = 0xf4a414fa.... If your SUPPORTED_MODELS is anything else ("llama3-8b:latest", "llama3:8b", "Llama3-8b"), the hash table miss causes every routed job to fail at stage 5 with:
ERROR job failed - stage 5 (resolve model): no local Ollama model configured for queued model ID "0xf4a414fa..."
The toolkit defaults SUPPORTED_MODELS=llama3-8b — the only string that matches what's registered on-chain. Do not change this unless you've also changed the on-chain registration via add-models.
On Docker Desktop for Windows, host.docker.internal often resolves to an IPv6 address (fdc4:f303:9324::254). Some Go HTTP clients (including the worker's) stick with the first resolved address, and the IPv6 path can stall. The toolkit defaults OLLAMA_URL to the explicit IPv4 http://192.168.65.254:11434 on Windows for exactly this reason.
WARN ollama model verification failed (non-fatal) - missing models on Ollama server: [llama3-8b]
This fires every startup and does not block anything. The worker does a strict-string-equality check against Ollama's /api/tags, which always reports tagged names (llama3-8b:latest). The actual inference call at stage 6 uses the bare name and Ollama resolves it correctly. Ignore the WARN — troubleshooting.md covers this in depth.
Even on a healthy, job-processing worker, this metric often stays at 0. It appears to be lazy/probe-updated rather than a continuous health signal. Don't panic — your worker is fine as long as websocket connected to gateway appears in the logs and you have no ERROR lines.
worker_heartbeat_last_emit_timestamp_seconds stays at 0 because the worker writes heartbeats to Redis, and the official docs never document a Redis URL. Best-effort interpretation: the gateway provides Redis credentials over the WS auth response if and when it needs them, and idle workers never get them. Doesn't block job processing.
The toolkit's 06-fund-worker.{ps1,sh} reads $env:FUNDER_PRIVKEY from your shell session if set, otherwise prompts via Read-Host -AsSecureString / read -rs. Your funder key never needs to touch secrets.{ps1,env} on disk. This is much safer than the official docs' "set FUNDER_PRIVKEY=..." approach.
If you see gsutil cp gs://lightchain-mainnet-backups-lightchain/... or AccessDeniedException 403, you've wandered into the chain-node install (Geth + Lighthouse). That requires private GCP access. Workers don't need any of it. This toolkit is the worker path — you're in the right place, just skip the chain-node detour.
See docs/troubleshooting.md for the full set of 16 named failure modes with symptoms and fixes.
# Day-to-day (Windows)
.\scripts\powershell\status.ps1 # on-chain status
.\scripts\powershell\tail-jobs.ps1 # watch jobs flow
docker logs -f lightchain-worker # all logs
.\scripts\powershell\stop.ps1 # graceful stop
.\scripts\powershell\08-run-worker.ps1 -NoTail # restart
.\scripts\powershell\sweep-rewards.ps1 -To 0xColdWallet # move rewards out
.\scripts\powershell\deregister.ps1 # reclaim 50k stake# Day-to-day (Linux/macOS)
./scripts/bash/status.sh
./scripts/bash/tail-jobs.sh
docker logs -f lightchain-worker
./scripts/bash/stop.sh
./scripts/bash/08-run-worker.sh --no-tail
./scripts/bash/sweep-rewards.sh 0xColdWallet
./scripts/bash/deregister.shFull operations runbook in docs/operations.md.
The worker key (WORKER_PRIVKEY) lives in scripts/{powershell,bash}/secrets.{ps1,env} so it can be read by the container at startup. The toolkit ACL-restricts that file to your user account on first run and generates a strong random keystore password automatically. The much higher-value funder key (FUNDER_PRIVKEY) is never written to disk — Phase 06 reads it from a session env var or prompts for it interactively. Both the keystore on disk and the in-memory env are local-host-only; nothing is sent over the network. The 50,000 LCAI stake is locked in the on-chain WorkerRegistry contract; rewards land in the worker wallet's balance and you sweep them to a cold wallet on whatever cadence suits your risk tolerance. Treat the worker wallet as hot working capital, not savings. Full threat model and hardening recommendations in docs/security.md.
PRs welcome — see CONTRIBUTING.md. The two highest-leverage contributions are (1) new entries in docs/troubleshooting.md for failure modes you hit and fixed, and (2) cross-platform compatibility improvements for OSes / distros we haven't tested.
Lint locally before pushing:
# Bash
shellcheck scripts/bash/*.sh
# PowerShell
Invoke-ScriptAnalyzer -Path scripts/powershell -Recurse -Severity Warning
# Markdown
npx markdownlint-cli2 "**/*.md"CI runs the same checks on every PR via .github/workflows/lint.yml.
MIT. Use this however you like. No warranty.
- The Lightchain team for building the protocol, the public worker image, and the runnable testnet.
- Foundry (
cast) for being a delight to script against. - Ollama for the cleanest "LLM-as-a-local-service" experience around.
- Everyone in the Lightchain Discord who's shared logs and tracebacks — that collective body of knowledge is half this repo.
Not affiliated with or endorsed by Lightchain. This is an independent community toolkit. The official documentation is at workers-testnet.lightchain.ai/run-node.