1. Run the stack
[⏱️ ~3 min] Watch a stack come up — ORK plus one registered device, no hardware required
Get started · 1 of 3 · Run the stack
If Kernel, Worker, manager, or thing are unfamiliar, read terminology.md first.
Overview
This is rung 1 of the Get started ladder: observe. It walks the shortest path from a fresh clone to a running Kernel with one mock Whatsminer registered, no hardware required. Everything runs in one Node process.
What you'll have at the end:
- A mock Whatsminer M56S serving telemetry on
127.0.0.1:14028 - An Kernel started and aware of one registered device
- Device list, telemetry, and available commands printed to the terminal
Prerequisites
- Node.js >=24 (LTS)
- npm >=11
HRPC relies on HyperDHT for peer connectivity, including when Kernel and the Worker share a process. Review the network requirements and checks if startup stalls.
Clone and install
1.1 Clone the repo
git clone git@github.com:tetherto/mdk.git
cd mdk1.2 Install dependencies
The monorepo has two workspaces with their own dependency trees. Install both:
backend/core/install-packages.sh ci
backend/workers/install-packages.sh ciEach script walks every package.json under its workspace and runs npm ci.
Run the example
node examples/backend/mdk-e2e/run.jsExpected output:
Devices: [ 'WM-001 [miner-wm-m56s]' ]
Telemetry: mining hashrate=295764693.45 power=4000W
Commands: reboot, setPowerMode, setLED, setupPools, setPowerPct, setUpfreqSpeed, registerThing, updateThing, forgetThings, saveSettings, saveComment, editComment, deleteCommentIf you see those three lines, the whole stack is up: mock device responding, Worker registered, Kernel started and aware of the device. The script queries the device and then exits cleanly.
If the example fails with EADDRINUSE, a previous run left port 14028 bound. Kill stale Node processes with pkill -f mdk-e2e and retry.
What just happened
Here is what run.js does — the minimum runnable shape for an MDK stack:
const { getKernel, waitForDiscovery } = require('../../../backend/core/mdk')
const { createMdkClient } = require('../../../backend/core/client')
const { startWhatsminerWorker } = require('../../../backend/workers/miners/whatsminer')
const wmMock = require('../../../backend/workers/miners/whatsminer/mock/server')
// Start a mock Whatsminer M56S on port 14028
wmMock.createServer({ port: 14028, host: '127.0.0.1', type: 'm56s', serial: 'WM-001', password: 'admin' })
// Start the Whatsminer Worker with one seeded device
const worker = await startWhatsminerWorker({
workerId: 'whatsminer-m56s-e2e',
model: 'm56s',
storeDir: path.join(ROOT, 'worker-store'),
kernelTopic: TOPIC,
seedDevices: [{
id: 'WM-001',
info: { serialNum: 'WM-001' },
opts: { address: '127.0.0.1', port: 14028, password: 'admin' }
}]
})
// Start Kernel — discovers the Worker via DHT
const kernel = await getKernel({ root: ROOT, topic: TOPIC })
await waitForDiscovery(kernel)
// Connect an MDK client over HRPC
const client = createMdkClient({ hrpc: { key: kernel.getPublicKey() } })
await client.connect()
// Query the device and print results
const list = await client.pullTelemetry(deviceId, 'list')
const tel = await client.pullTelemetry(deviceId, 'metrics')
const caps = await client.getCapabilities(deviceId)Six steps, in order:
- Starts a mock miner.
wmMock.createServer({ port: 14028, type: 'm56s', serial: 'WM-001', password: 'admin' })binds port 14028 with a Whatsminer-compatible API serving canned telemetry. - Starts a Worker.
startWhatsminerWorker({ ...seedDevices })instantiates the Whatsminer manager and seeds it with one device registration at127.0.0.1:14028. The Worker stores the registration and begins polling the mock. - Starts Kernel.
getKernel({ topic })brings up the kernel and joins the DHT topic. It discovers the Worker and pulls its identity and capabilities. - Waits for discovery.
waitForDiscovery(kernel)blocks until the Worker appears in Kernel's registry. - Connects a client.
createMdkClient({ hrpc: { key: kernel.getPublicKey() } })creates an MDK Protocol client that speaks to Kernel over HRPC. - Queries and prints. The script pulls device list, telemetry, and capabilities, prints them, cleans up, and exits.
No Gateway here? Right. Gateway is the translator that lets non-Node consumers — browser UIs, AI agents over MCP — speak MDK Protocol to Kernel. This script holds the Kernel handle in-process and speaks MDK Protocol to it directly. See architecture.md#gateway for when Gateway is mandatory.
Cleanup
The script cleans up automatically on exit. Temporary data lives at os.tmpdir()/e2e-run/ and is removed by the script. If needed, you can manually clean with:
rm -rf "$TMPDIR/e2e-run" /tmp/e2e-runContinue
Next: 2. Control devices from the CLI — keep a stack running and drive it interactively (Whatsminer plus an MDK REPL).
Next steps
- Try different miner hardware — the same MDK API works with Antminers, Avalonminers, and more
- Run a full site (5 Workers, 26 devices) —
examples/backend/mdk-site/site.js - See Kernel and a Worker as separate OS processes —
dht-worker.js+dht-kernel.js+client.js - Understand the install pattern for any Worker —
backend/workers/docs/install-pattern.md - Read all runnable examples in one place —
examples/backend/README.md