MDK Logo

App Node

The App Node as a development canvas — extension model, data access, auth design, and ORK connection

Overview

This page introduces the App Node surface. It explains what concerns it owns, how to extend it with plugins and routes, how data flows from ORK 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 App Node 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 App Node owns

The App Node wraps @tetherto/mdk-client — the MDK protocol connector to ORK — and adds an authenticated HTTP, WebSocket, and MCP interface on top. Consumers connect through the App Node; using @tetherto/mdk-client without the App Node is not supported by this monorepo.

The App Node owns three concerns that ORK 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 ORK
  • 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 ORK

ORK 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 App Node (which wraps @tetherto/mdk-client internally) when using the toolkit.

Extension model

The App Node 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 startAppNode() 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 startAppNode(). 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.

Use an alternative gateway

If your use case does not need the App Node's HTTP surface, RBAC, or plugin system — for example, a background service that only dispatches commands — you can use @tetherto/mdk-client directly against ORK without running the App Node at all. This is the direct path. Such an approach is not directly supported by this monorepo, as most applications build on the App Node.

Data access

Two services are available inside every plugin controller.

services.mdkClient gives access to live ORK data: pull a telemetry snapshot, dispatch a command, list registered workers. It's null when the App Node starts without a live ORK 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 ORK round-trip.

The split exists because the two sources have different latency and availability characteristics. mdkClient calls are network operations that can fail if ORK is unreachable. dataProxy reads from local storage and remains available whether ORK is online or not.

Authentication design

The App Node validates a JWT Bearer token before proxying any request to ORK. By design, ORK does not perform user-level authentication. For IPC (same-host), the socket itself provides implicit trust and no key exchange is required. For HRPC (remote ORK), ORK maintains an allowlist; pre v1.0 it is opt-in (the default auth.whitelist is empty and admits any caller), but when configured the App Node's DHT public key must be added before the connection is accepted. Either way, once the transport is established, ORK trusts all messages from the App Node without inspecting user identity.

User authentication and RBAC are entirely the App Node'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.

ORK connection

The App Node is the active side of this connection — it dials ORK. ORK is the passive listener; it does not initiate contact with the App Node.

Two transports are available:

  • IPC (default): App Node dials ORK over a Unix socket on the same host. Low-latency, implicit trust — no allowlisting required. The default for single-host deployments
  • HRPC: App Node dials ORK over encrypted Hyperswarm streams. The App Node's DHT public key must be added to ORK's auth.whitelist before the connection is accepted. Used when ORK and the App Node run on separate hosts

Pre v1.0, the allowlist is opt-in. ORK's auth.whitelist defaults to empty, which admits any HRPC caller. When an allowlist is configured, the App Node's DHT public key must appear in it before ORK accepts the connection.

Next steps

On this page