Architecture¶
On narrow screens, swipe tables horizontally to see every column.
The repository has three runtime components: a Burrow mod server, a settings contribution, and a standalone gateway package. Node Goblin remains an independent mod. Core owns the generic execution-provider contract, agent assignment, host admission, and permission decisions.
Component map¶
flowchart TB
subgraph Controller[Burrow controller]
UI[Host settings renderer] --> UIJS[ui/settings.js]
UIJS --> API[Mod HTTP routes]
CORE[Core processExecution contract] --> ADAPT[Process and filesystem adapter]
API --> SVC[Controller service]
ADAPT --> SVC
SVC --> TLS[GatewayControllerListener]
SVC --> SETTINGS[Mod settings and secrets]
end
subgraph Host[Execution host]
NET[OutboundGatewayTransport] --> DAEMON[GatewayDaemon]
DAEMON --> PROC[runProcess]
DAEMON --> FILES[runFilesystem]
DAEMON --> JOURNAL[OperationJournal]
NET --> IDENTITY[Identity and trust files]
end
NET <-->|TLS JSON lines| TLS
On narrow screens, scroll the diagram horizontally to read all labels.
Manifest and activation¶
burrow.mod.json declares ID node-goblin, system: true, server/index.mjs, ui/settings.js, the API-target contribution, and the privileged execution-provider-v1 capability. The version appears in the manifest and gateway package/release files.
A system mod uses the same install, update, enable, disable, and uninstall lifecycle as any other mod. system: true is a trust classification: it permits the manifest to request explicitly named privileged Core capabilities that regular mods cannot request. It does not grant every privileged interface, change installation mechanics, imply that the mod is bundled, or make it non-removable. Core exposes only the declared capabilities it supports and rejects undeclared or unsupported requests.
Node Goblin requests only execution-provider-v1. That capability lets it register the controller-side process and native-filesystem execution provider. It does not grant privileged settings, diagnostics, mod-management, database, arbitrary filesystem, or arbitrary process access inside the Burrow controller. Core continues to own target admission, agent assignment, routing, and permission decisions.
activate() creates or accepts a controller service, registers a Core process controller when the declared processExecution.registerController interface exists, and registers the mod's HTTP routes. Its returned close() unregisters the controller and closes the listener.
The adapter exposes executeProcess(request, { abortSignal }) and executeNativeFilesystem(request, { abortSignal }). Core supplies correlated operation IDs and target IDs. The target ID becomes the gateway ID. Process results are translated into the shell_exec result shape; filesystem results preserve their tool result and add remote execution provenance.
The Core process adapter forwards the execution form (command or executable/args), cwd, env, timeoutMs, and supported protected-value/binding inputs. It does not forward the wire protocol's deadlineMs or maxOutputBytes options in this baseline. Direct HTTP process dispatch and direct protocol callers have a different option surface; do not assume every wire option is available through the Core adapter.
Controller service¶
The service reads listener settings, gateway trust records, pairing identity, and TLS secrets. It generates missing controller pairing keys and, when enabled, missing TLS material. It wraps the TLS listener with persistence and route-facing state.
Pairing approval first stores the trusted public key and gateway record, removes the pending record, and then commits the live approval. Persistence failures attempt rollback. Stored pending requests alone are informational after restart; the live nonce and pairing generation must match. Pending records have no automatic age-based expiry. The live connection has a 60-second authentication timeout by default, including time awaiting approval; a reconnect produces a new challenge and pairing code. A listed pending request therefore does not prove that its challenge is still live.
Connection and execution lifecycle¶
stateDiagram-v2
[*] --> Authenticating
Authenticating --> Pending: New signed node identity
Pending --> Ready: Operator approval
Authenticating --> Ready: Known identity proves possession
Pending --> Closed: Rejected or disconnected
Ready --> Closed: Socket closes or identity revoked
Closed --> Authenticating: Host reconnects
On narrow screens, scroll the diagram horizontally to read all labels.
The host transport reconnects with exponential backoff from 100 ms to a maximum 5 seconds. Authentication success resets the attempt counter. A disconnected controller rejects pending dispatch promises with gateway_disconnected; that is uncertainty about execution completion, not a rollback.
The daemon validates a request, checks completed replay and active-operation state, sends accepted, runs the operation, stores terminal evidence in the journal, then sends the final response. A duplicate active ID returns operation_in_progress; a reused ID with a different digest returns operation_id_conflict.
Persistence ownership¶
| Owner | Stored data | Purpose |
|---|---|---|
| Burrow mod settings | Controller config, API targets, gateway metadata, pending pairings, TLS metadata, operation correlation | Controller-side configuration and correlation |
| Burrow mod secrets | TLS private material, pairing identity, trusted public keys, HMAC secrets | Credential/identity storage supplied by the host |
| Gateway state directory | Node identity, controller trust/pins, enrollment-consumed marker, current pairing code, completed operations | Durable host identity and bounded replay |
| Listener memory | Active connections, pending requests, bounded activity | Current connection/dispatch bookkeeping |
Controller operation correlation keeps up to 256 terminal records by default and does not expire unresolved records. It stores digests and terminal references, not a recoverable copy of gateway outcomes; it does not extend the gateway replay window. Listener activity is a separate, bounded in-memory view.
Core storage implementation and encryption guarantees are outside this repository. Back up the Core-owned secrets store with its required keys, not just the mod's visible settings. See configuration for names and paths.