Run a mining site end to end
[⏱️ ~3 min] From git clone to a live site with multiple Workers, mock hardware, and a browser dashboard
If Kernel, Gateway, Worker, manager, or thing are unfamiliar, read terminology first.
Overview
This tutorial runs the Starter site example end to end: a Whatsminer worker, an Ocean pool worker, and a SATEC powermeter worker, each backed by mock hardware that speaks the real wire protocol, a Gateway HTTP API, and a React dashboard, all supervised by PM2.
What you'll have at the end:
- Mock miners, a mock pool, and a mock powermeter, each driven by the real Worker driver code against a localhost mock instead of hardware
- A Gateway API on
:3000serving/site/overview,/site/history,/site/miners/:id/command, and/site/miners/:id/pools - A React dashboard on
:3000with Dashboard, Containers, Monitoring, Pools, and Control pages - Two MCP surfaces exposing the site as tools for AI agents: the Gateway's auto-exported routes on
:3100, and a hand-authored, agent-contract tool set on:3101
Every component above runs as its own PM2-supervised OS process, discovering the Kernel over a shared local directory rather than a DHT.
Prerequisites
- Node.js >=24 (LTS)
- npm >=11
- PM2 (
npm install -g pm2)
Install the example
1.1 Clone the repo
git clone git@github.com:tetherto/mdk.git
cd mdk1.2 Run setup
cd examples/mvp-site
npm run setup
npm run setup:configsetup installs backend/core, backend/workers, the UI workspace devkit packages, and this example's own dependencies. setup:config copies the committed *.json.example config files
into place without overwriting any that already exist.
The script walks several workspaces; first run takes 1-2 minutes.
Start the site
npm startstart.js generates deploy/ecosystem.config.js from config/site.deploy.json, starts the PM2 apps, then exits — PM2 itself keeps the processes running in the background.
pm2 list # mocks, mocks-ocean, mocks-satec, kernel, worker, worker-ocean, worker-satec, gateway, mcpWait for every app to show online, then open http://localhost:3000/ in a browser. The dashboard shows live hashrate, power, and
per-device status.
PM2 showing every app online means the processes started, not that the site has fully settled. The Ocean pool worker ticks its
mock fetch/save cycle every 10 seconds, and Kernel's local-discovery scan runs every 4 seconds, so /site/overview can report
"pools": 0 for 15-20 seconds after pm2 list goes green. Retry the command below if the first response comes back short.
Verify via the API:
curl -s http://localhost:3000/site/overview | jq '{miners: (.miners|length), pools: (.pools|length), powermeters: (.powermeters|length)}'Expected output:
{
"miners": 5,
"pools": 1,
"powermeters": 1
}config/devices.json seeds five miners and one powermeter by default. Add
or remove entries there to resize the fleet; see configure devices for the format.
(Optional) Explore the UI pages
The Dashboard, Containers, Monitoring, Pools, and Control pages are all served from ui/dist via the Gateway on the same :3000
port. The Pools page reads the Ocean pool worker's stats; the Monitoring page charts the SATEC meter's power series over time. See
UI pages for what each one shows.
(Optional) Connect an AI agent over MCP
There are a range of agents and connection modes; apply the method for your agent. For Claude CLI:
cd examples/mvp-site
claude
Accept Use this MCP server
Then your agent can query and act on the site's devices.
The example exposes MCP two ways, both listed in .mcp.json.example:
- The Gateway auto-exports its own
/site/*routes as tools, on:3100 - A hand-authored tool set with agent-contract metadata —
backend/mcp-plugins/site/mcp-plugin.json— on:3101, giving an agent summary-first, closed-vocabulary tools (summarize_site,count_devices,list_devices,get_device,rank_devices,act_device) instead of raw route exports
Point an MCP client at either URL and it can query the fleet or, for the second surface, act on it with operator approval.
Stop the site
npm run stop:pm2This stops and removes every PM2-managed process for this site: mocks, Workers, Kernel, Gateway, and the MCP servers.
The PM2 daemon itself (pm2 list still shows a God Daemon process) stays running in the background after this — that's expected,
since PM2 manages processes across every project on the machine, not just this one. If it ever becomes unresponsive, see
stale PM2 processes for pm2 kill, which stops PM2 itself, not just this site.
What just happened
- Setup installed
backend/core,backend/workers, the MDK UI devkit, and this example, thensetup:configseeded its local config from the committed*.examplefiles. - Mock hardware: PM2's
mocks,mocks-ocean, andmocks-satecroles each started a mock device server — a Whatsminer miner, an Ocean pool, and a SATEC powermeter — speaking the real wire protocol, so the Worker drivers run their true connect, collect, and command paths against them. - Kernel: the
kernelrole started the orchestration layer in local-discovery mode, watching a shared directory for Workers to publish their RPC keys to. - Workers: the
worker,worker-ocean, andworker-satecroles each dispatched to that family's boot function (startWhatsminerWorker,startOceanPoolWorker,startSatecWorker) to construct aWorkerRuntime, seed its devices fromconfig/devices.json, and publish its RPC key for the Kernel to discover. - Gateway: the
gatewayrole mounted the site plugin declared bybackend/gateway-plugins/site/mdk-plugin.jsonand the built UI fromui/dist, then opened the HTTP server on:3000. The plugin aggregates data across the three Workers throughmdkClient. - MCP: the
mcprole started both MCP surfaces — the Gateway's auto-exported tools on:3100, and the hand-authored agent-contract tool set on:3101.
Resetting state
State (Kernel key, Worker seeds, device registry) persists in .site-data/. If you change config/devices.json after the first
run, reset it before restarting — seed devices are only registered once, on an empty store.
Next steps
- Fix a failed boot, a port clash, or stale PM2 processes with the example's troubleshooting section
- Register real hardware instead of mocks by configuring devices
- Build the same shape from an empty directory with one Worker and one route: build a minimal single-page dashboard
- Serve your own data by adding custom plugins to the Gateway HTTP API
- Integrate your own hardware by building a third-party Worker
- Run the same PM2-supervised model as a production deployment, or across hosts with DHT discovery, with the supervised-services deployment guide
- Go beyond querying the site's MCP tools by hand: connect the conversational operator agent, which calls the same tools and gates writes behind human approval
- See the full 11-worker fleet — three miner families, containers, sensors, and two pools — in
examples/full-site - Browse every runnable example in one place