Gateway
The Gateway as a development canvas — extension model, data access, auth design, and Kernel connection
Overview
This page introduces the Gateway surface. It explains what concerns it owns, how to extend it with plugins and routes, how data flows from Kernel to your controllers, and why authentication lives here rather than in the kernel. Read this before building plugins, auth flows, or aggregation routes on top of MDK.
The Gateway is the backend layer of the MDK App Toolkit, which aligns the plugin system and frontend packages into the supported development path for this monorepo.
What the Gateway owns
The Gateway wraps @tetherto/mdk-client — the MDK protocol connector to Kernel — and adds an authenticated HTTP,
WebSocket, and MCP interface on top. Consumers connect through the Gateway; using @tetherto/mdk-client without the Gateway
is not supported by this monorepo.
The Gateway owns three concerns that Kernel deliberately does not handle:
- Authentication and RBAC: JWT validation, session management, OAuth2 (Google and Microsoft built in), and role-based access before any request reaches Kernel
- API surface: REST endpoints, WebSocket telemetry subscriptions, command dispatch, and the MCP endpoint for AI agents
- Fleet aggregation: cross-Worker queries that compute site hashrate, average temperature, and cross-rack efficiency — resolved in controller code, not in Kernel
Kernel is a pass-through kernel. It routes commands to Workers, collects telemetry, and maintains
the device registry. Everything above the kernel — authentication, business logic, API surface — is owned by the caller:
the Gateway (which wraps @tetherto/mdk-client internally) when using the toolkit.
Extension model
The Gateway offers two ways to add routes, in order of preference.
1. Plugin system
The recommended path. A plugin is a directory with an mdk-plugin.json manifest and one or more controller files.
Pass the directory path to startGateway() via extraPluginDirs.
Controllers receive a services bag on every request — mdkClient, dataProxy, authLib, and conf — with no protocol knowledge required.
The default plugins (auth, telemetry, site-hashrate) load the same way as any plugin you write.
The plugin authoring guide covers the build process end to end. The plugin reference documents the manifest schema, controller contract, and loader errors.
2. Raw Fastify routes
For one-off handlers that do not need a manifest, pass additionalRoutes to startGateway(). These are plain Fastify route objects —
no services injection, no manifest validation, no auth wiring. Use this path sparingly; a plugin is easier to test in isolation
and easier for a later maintainer to follow.
Connect without the Gateway
If your use case does not need the Gateway's HTTP surface, RBAC, or plugin system — for example, a background service that only
dispatches commands — you can use @tetherto/mdk-client directly against Kernel without running the Gateway at all.
This is the direct path. Such an approach is not directly supported by this monorepo, as most applications build on the Gateway.
Data access
Two services are available inside every plugin controller.
services.mdkClient gives access to live Kernel data: pull a telemetry snapshot, dispatch a command, list registered Workers.
It's null when the Gateway starts without a live Kernel connection, so guard it before use.
services.dataProxy reads from persisted Worker tail-logs: time-series aggregation, historical hashrate, efficiency trends.
Use this for data that does not require a live Kernel round-trip.
The split exists because the two sources have different latency and availability characteristics. mdkClient calls are network
operations that can fail if Kernel is unreachable. dataProxy reads from local storage and remains available whether Kernel is online or not.
Authentication design
The Gateway validates a JWT Bearer token before proxying any request to Kernel. By design, Kernel does not perform user-level authentication.
The HRPC connection is an encrypted Noise channel, and Kernel maintains an allowlist; pre v1.0 it is opt-in (the default
auth.whitelist is empty and admits any caller), but when configured the Gateway's DHT public key must be added before the connection is accepted.
Once the transport is established, Kernel trusts all messages from the Gateway without inspecting user identity.
User authentication and RBAC are entirely the Gateway's responsibility. RBAC is enforced at the route level via the permissions
field in mdk-plugin.json. Routes with "auth": false are public — no JWT is required. Routes with "auth": true but no permissions
array are accessible to any authenticated user.
A permissions array restricts access further to users with matching roles.
Kernel connection
The Gateway is the active side of this connection — it dials Kernel. Kernel is the passive listener; it does not initiate contact with the Gateway.
The connection is Hyperswarm RPC (HRPC) — an encrypted peer-to-peer transport addressed by Kernel's public key. What varies is how the Gateway obtains that key:
- Same host (zero-config default): Kernel publishes its HRPC public key to a well-known key file (
<tmpdir>/mdk/.kernel-key) on start; the Gateway reads it from there automatically when no key is passed - Separate hosts: pass the key explicitly (
startGateway({ kernelKey })), obtained fromkernel.getPublicKey()on the Kernel host. When Kernel'sauth.whitelistis configured, the Gateway's DHT public key must be added to it before the connection is accepted
Pre v1.0, the allowlist is opt-in. Kernel's auth.whitelist defaults to empty, which admits any HRPC caller. When an allowlist
is configured, the Gateway's DHT public key must appear in it before Kernel accepts the connection.