MDK Logo

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.

Dashboard Workbench — frozen previewcaptured 2026-08-14 · ui-dash-for-ux@1fc1023c

Site Ops

BrowserGatewayKernelWorkerMock device
Worker
mock-temperature
READYHEALTHY1 devicekey a6b385dd
mock-temperature-0
24.95°C
OK

Explore

Question

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.

Reproduction steps
  1. Capture a baseline while the example is running.
  2. Stop the whole example: Ctrl+C in the terminal running `npm start`.
  3. Start it again: `npm start`, without deleting the `.mdk/` directory.
  4. The verdict updates once the run id differs from the baseline.
Answer
Verified

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.

Query
curl -s http://localhost:3847/api/dashboard-workbench/identity | jq .
Before restart
After restart
Limitations
  • 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

docs://examples/dashboard-workbench/docs/data-access.mdread-only

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.

DataMDK CLI 0.7Dashboard workbench HTTPGateway code
Kernel IDFull public keySHA-256 fingerprintNot needed
Worker ID and statusYesYeslistWorkers()
Worker telemetryNoYesgetCapabilities() 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 .kernel
docs://examples/dashboard-workbench/workers/mock-temperature/README.mdread-only

mock-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:

DataMDK CLI 0.7Dashboard workbench HTTP
Kernel IDFull public keySHA-256 fingerprint
Worker ID and statusYesYes
Worker telemetryNoYes

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 .kernel

workers/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

On this page