Inspect a running MDK stack with the dashboard workbench
[⏱️ ~5 min] What examples/dashboard-workbench shows you, and how to run it yourself
examples/dashboard-workbench pairs a real MDK stack — one Kernel, one Gateway, one contract-first mock-temperature Worker — with a dashboard whose job is to help you understand a running stack, not to operate a site. It's a local dev tool: you run it on your own machine, not a hosted demo.
The preview below is frozen — real output captured from an actual run, not a live connection. Nothing on this page talks to a Kernel, Gateway, or Worker. Run it yourself (see above) to see it live.
Site Ops
Explore
Does the Kernel's key file survive shutdown, keeping the same identity after a restart?
The Kernel is addressed by its public key — over HRPC, in the Gateway's connection, and as the file the Gateway reads to reconnect (.mdk/kernel.key). If that key changed on every restart, nothing that references the Kernel by identity would survive a routine restart.
- Capture a baseline while the example is running.
- Stop the whole example: Ctrl+C in the terminal running `npm start`.
- Start it again: `npm start`, without deleting the `.mdk/` directory.
- The verdict updates once the run id differs from the baseline.
The example was fully stopped and restarted (the run id changed), and the Kernel's fingerprint captured in the baseline is still exactly equal. The Kernel's identity survives a restart.
curl -s http://localhost:3847/api/dashboard-workbench/identity | jq .- Scoped to the Kernel only. The Worker's fingerprint is shown for transparency, but a contract-first Worker's RPC key is not currently persisted across a restart by any current caller of WorkerRuntimeV2 (a known, documented gap) — so it's expected to differ every boot and isn't part of the verdict.
- Deleting .mdk/ between captures produces the same “Changed” verdict as a genuine break.
Learn
Overview
This example wires up two ways to inspect its running MDK stack:
- Use the MDK CLI to inspect stack processes and identities.
- Use the example's Gateway HTTP routes to read data that an application can use.
The browser and curl call the Gateway. They do not call the Kernel or Worker directly. The Gateway uses @tetherto/mdk-client to ask the Kernel for Worker data.
MDK also ships a standalone MCP server (@tetherto/mdk-mcp) that exposes stack data to AI agents as tools over the same client the Gateway uses. It isn't wired into this example, and the CLI's own mdk mcp register command is currently a stub — a third, not-yet-available way to reach this stack, not a gap in the two above.
| Data | MDK CLI 0.7 | Dashboard workbench HTTP | Gateway code |
|---|---|---|---|
| Kernel ID | Full public key | SHA-256 fingerprint | Not needed |
| Worker ID and status | Yes | Yes | listWorkers() |
| Worker telemetry | No | Yes | getCapabilities() and pullTelemetry() |
Kernel ID
The example's identity route returns a SHA-256 fingerprint instead of the full key. Use the fingerprint to compare identity without displaying the key:
curl -s http://localhost:3847/api/dashboard-workbench/identity | jq .kernelmock-temperature
An MDK Worker Plugin for the dashboard-workbench example — a read-only mock temperature sensor. It ships only an mdk-contract.json (its capability manifest) plus src/ handler files, a device "vendor SDK" client, and a mock device server.
Its one telemetry channel (temperature) is the only thing the Site Ops tab reads. There are no commands — this sensor is intentionally read-only.
The mock reports a value in the 22-28°C range with a small random-walk jitter per read. Set MDK_WORKBENCH_MOCK_SEED to any string/number to make the sequence deterministic; unset, it uses Math.random().
Inside each tab
Real output from an actual run — the exact commands and responses, not a paraphrase.
Site Ops
curl -s http://localhost:3847/api/dashboard-workbench/overview | jq .{
"observedAt": 1786713843540,
"kernel": { "connected": true },
"gateway": { "serving": true },
"worker": {
"id": "mock-temperature",
"state": "READY",
"health": "HEALTHY",
"deviceCount": 1,
"publicKeyFingerprint": "a6b385dd"
},
"sensor": {
"id": "mock-temperature-0",
"temperature": { "value": 24.95, "unit": "°C" },
"health": "OK"
}
}Explore
What the CLI can tell you today, next to what this example's Gateway route returns:
| Data | MDK CLI 0.7 | Dashboard workbench HTTP |
|---|---|---|
| Kernel ID | Full public key | SHA-256 fingerprint |
| Worker ID and status | Yes | Yes |
| Worker telemetry | No | Yes |
MDK also ships a standalone MCP server (@tetherto/mdk-mcp) that exposes stack data to AI agents as tools over the same client the Gateway uses — but it isn't wired into this example, and the CLI's own mdk mcp register command is currently a stub. Treat it as a third, not-yet-available way to reach this stack, not a gap in the two above.
mdk status -o json | jq '.stack.workers.items[]'Reports the Worker's id, state, health, and device count — no live temperature. mdk get exists in the CLI's command surface, but its handler is currently a placeholder.
curl -s http://localhost:3847/api/dashboard-workbench/overview | jq .sensor{
"id": "mock-temperature-0",
"temperature": { "value": 24.95, "unit": "°C" },
"health": "OK"
}The Gateway route is the only current way to read the live temperature — the CLI can't yet.
The identity claim, before and after a full stop/restart (.mdk/ kept):
curl -s http://localhost:3847/api/dashboard-workbench/identity | jq .// before restart
{
"runId": "1786713822982-070656",
"kernel": {
"publicKeyFingerprint": "sha256:693208754d8e5e630125d0ca9d5b149cd08ecac6194fa8602c566a91324708c4",
"keyFile": ".mdk/kernel.key"
},
"workers": [
{
"id": "mock-temperature",
"publicKeyFingerprint": "sha256:a6b385dd2b734160739f03e04cd65b83ff7a329d7845af4695238770ef7588c6",
"keyFile": null
}
]
}// after restart — same .mdk/, no deletion
{
"runId": "1786713880862-9ee764",
"kernel": {
"publicKeyFingerprint": "sha256:693208754d8e5e630125d0ca9d5b149cd08ecac6194fa8602c566a91324708c4",
"keyFile": ".mdk/kernel.key"
},
"workers": [
{
"id": "mock-temperature",
"publicKeyFingerprint": "sha256:9f9a5ee12f64f61577476f77204ef34f060868e7476410e6914cc9b5da8ed43c",
"keyFile": null
}
]
}runId changed (a new launch), the Kernel's fingerprint is exactly equal, and the Worker's fingerprint changed. That last part is a known, documented gap, not a bug in this example: contract-first Workers (WorkerRuntimeV2, the only Worker shape mdk run supports) don't yet persist their RPC key across a restart — only the Kernel does.
Learn
Renders checked-in documentation from its real source at build time — no copy is committed separately. Two excerpts, verbatim:
docs/data-access.md:
## Kernel ID
The example's identity route returns a SHA-256 fingerprint instead of the full key. Use the fingerprint to compare
identity without displaying the key:
curl -s http://localhost:3847/api/dashboard-workbench/identity | jq .kernelworkers/mock-temperature/README.md:
Its one telemetry channel (`temperature`) is the only thing the Site Ops tab
reads. There are no commands — this sensor is intentionally read-only.
The mock reports a value in the 22-28°C range with a small random-walk jitter
per read. Set `MDK_WORKBENCH_MOCK_SEED` to any string/number to make the
sequence deterministic; unset, it uses `Math.random()`.Source
Full source, including the Worker contract, the Gateway plugin, and the test suite:
View examples/dashboard-workbench on GitHub