Sample Configurations
Complete, runnable configuration documents for the common deployment shapes. Each is a full JSON file you
can drop in as -c FILE <path> (and, for MQTT transport, as --transport MQTT <path>). For the meaning
of every field see reference — configuration; for the deployment steps see
the how-to guides.
All console-specific knobs live under component.global.console; every field is optional (lenient
parsing with defaults), so a real config is often much shorter than these annotated examples.
1. Minimal HOST (dev rig)
Section titled “1. Minimal HOST (dev rig)”The smallest useful config — essentially the test-configs/config.json in the repo. It points at a
local site broker and otherwise takes the console defaults; the one exception is
console.ws.bindAddress, set to 0.0.0.0 to accept connections from other hosts (the default is
loopback 127.0.0.1). Run it with:
target/release/edge-console-gateway --platform HOST --transport MQTT ./config.json -c FILE ./config.json -t site-console{ "logging": { "level": "INFO" }, "heartbeat": { "enabled": true, "intervalSecs": 5 }, "metricEmission": { "target": "messaging" },
"messaging": { "local": { "host": "localhost", "port": 1884, "clientId": "edge-console" }, "requestTimeoutSeconds": 30 },
"hierarchy": { "levels": ["site", "device"] }, "identity": { "site": "dallas" },
"component": { "global": { "console": { "ws": { "port": 8443, "bindAddress": "0.0.0.0", "heartbeatIntervalMs": 15000 }, // bindAddress 0.0.0.0 = remote-access opt-in; default is loopback 127.0.0.1 "staleness": { "warnMultiplier": 2, "staleMultiplier": 2.5, "offlineMultiplier": 5, "defaultIntervalSecs": 5, "sweepIntervalMs": 1000 }, "cache": { "maxChannelsPerComponent": 1024 }, "runtime": { "workerThreads": 4, "mallocArenaMax": 2, "eventBufferCapacity": 512 } } }, "instances": [{ "id": "main" }] }}2. Self-contained built deployment (gateway serves the UI — no Vite/nginx)
Section titled “2. Self-contained built deployment (gateway serves the UI — no Vite/nginx)”Add console.ws.webRoot pointing at the built ui/dist. The one process now serves the WebSocket and
the UI on :8443. Browse straight to http://<host>:8443/.
{ "logging": { "level": "INFO" }, "heartbeat": { "enabled": true, "intervalSecs": 5 }, "metricEmission": { "target": "messaging" },
"messaging": { "local": { "host": "site-broker.internal", "port": 1883, "clientId": "edge-console" }, "requestTimeoutSeconds": 30 },
"hierarchy": { "levels": ["site", "device"] }, "identity": { "site": "dallas" },
"component": { "global": { "console": { "ws": { "port": 8443, "bindAddress": "0.0.0.0", "heartbeatIntervalMs": 15000, // Relative to the gateway process cwd (e.g. run from <repo> -> "ui/dist"), // or an absolute path to a built ui/dist. "webRoot": "ui/dist" } } }, "instances": [{ "id": "main" }] }}Build first (npm run build produces ui/dist). Put a TLS terminator in front for HTTPS/WSS — the gateway
is plain HTTP either way.
3. Locked-down RBAC + command deadlines
Section titled “3. Locked-down RBAC + command deadlines”A production-leaning policy: unauthenticated connections default to a read-only viewer role;
operator has full control except reboot. Command deadlines are tuned per verb.
{ "logging": { "level": "INFO" }, "heartbeat": { "enabled": true, "intervalSecs": 5 }, "metricEmission": { "target": "messaging" },
"messaging": { "local": { "host": "site-broker.internal", "port": 1883, "clientId": "edge-console" }, "requestTimeoutSeconds": 30 },
"hierarchy": { "levels": ["site", "device"] }, "identity": { "site": "dallas" },
"component": { "global": { "console": { "ws": { "port": 8443, "bindAddress": "0.0.0.0", "webRoot": "../ui/dist" },
"rbac": { "defaultRole": "viewer", "roles": { "operator": { "allow": ["*"], "deny": ["reboot"] }, "viewer": { "allow": ["ping", "get-configuration"] } } },
"commands": { "defaultTimeoutMs": 30000, "maxTimeoutMs": 60000, // hard ceiling = the uns-bridge reply-map TTL "verbTimeouts": { "ping": 10000, "reload-config": 45000 } } } }, "instances": [{ "id": "main" }] }}Reminder: RBAC enforcement is real, but the console does not resolve a connecting principal —
defaultRoleapplies to every connection, and the read surface is unauthenticated. Keep the console on a trusted network. See explanation → security.
4. High-volume site — tuned caches and a snappier ladder
Section titled “4. High-volume site — tuned caches and a snappier ladder”A busy fleet with hundreds of components and chatty telemetry. The staleness ladder is tightened (a 5 s cadence should trip OFFLINE faster), and the store bounds are raised to hold more history without letting any one component evict others.
{ "logging": { "level": "WARN" }, "heartbeat": { "enabled": true, "intervalSecs": 5 }, "metricEmission": { "target": "messaging" },
"messaging": { "local": { "host": "site-broker.internal", "port": 1883, "clientId": "edge-console" }, "requestTimeoutSeconds": 30 },
"hierarchy": { "levels": ["site", "area", "line", "device"] }, "identity": { "site": "dallas", "area": "assembly", "line": "5" },
"component": { "global": { "console": { "ws": { "port": 8443, "bindAddress": "0.0.0.0", "webRoot": "../ui/dist" },
"staleness": { "warnMultiplier": 1.5, "staleMultiplier": 2, "offlineMultiplier": 3, // must stay strictly increasing warn<stale<offline "defaultIntervalSecs": 5, "sweepIntervalMs": 500 // recompute twice a second },
"cache": { "maxChannelsPerComponent": 4096 }, "events": { "maxEvents": 5000, "maxPerComponent": 250 }, "metrics": { "maxSeriesPoints": 120, "maxSeries": 8000 }, "logs": { "maxRecords": 10000, "maxPerComponent": 2000, "defaultTail": 500, "maxTail": 2000 }, "runtime": { "workerThreads": 4, "mallocArenaMax": 2, "eventBufferCapacity": 512 } } }, "instances": [{ "id": "main" }] }}Note the four-level hierarchy/identity — that is the console’s own identity. The console renders
each observed component’s own declared hierarchy dynamically, regardless of its own.
runtime.workerThreads and runtime.mallocArenaMax are launch-latched. Set matching
EDGECONSOLE_WORKER_THREADS and MALLOC_ARENA_MAX environment values before starting the gateway.
runtime.eventBufferCapacity bounds the hidden live-event ring retained for connected WebSocket
sessions.
5. Kubernetes (ConfigMap) + a Service/Ingress sketch
Section titled “5. Kubernetes (ConfigMap) + a Service/Ingress sketch”On Kubernetes the config comes from a mounted ConfigMap (-c defaults to CONFIGMAP), identity from the
Downward API, logging is stdout JSON, and /healthz is the probe. The console is a single replica
(long-lived WebSockets + in-memory model). You provide the Service + Ingress that reaches its WebSocket
port — no Helm chart is included.
The console config (the ConfigMap payload):
{ "logging": { "level": "INFO" }, "heartbeat": { "enabled": true, "intervalSecs": 5 }, "metricEmission": { "target": "prometheus" },
"messaging": { "local": { "host": "emqx.messaging.svc.cluster.local", "port": 1883, "clientId": "edge-console" }, "requestTimeoutSeconds": 30 },
"component": { "global": { "console": { "ws": { "port": 8443, "bindAddress": "0.0.0.0", "webRoot": "/app/ui/dist" }, "rbac": { "defaultRole": "viewer", "roles": { "operator": { "allow": ["*"] }, "viewer": { "allow": ["ping", "get-configuration"] } } } } }, "instances": [{ "id": "main" }] }}A minimal Service + Ingress (TLS terminated at the Ingress) reaching the console. This is illustrative — adapt to your cluster:
apiVersion: v1kind: Servicemetadata: name: edge-consolespec: selector: { app: edge-console } ports: - name: ws port: 8443 targetPort: 8443---apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: edge-console annotations: # WebSocket upgrades must be allowed through; TLS terminates here. nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"spec: tls: - hosts: [console.dallas.example.com] secretName: edge-console-tls rules: - host: console.dallas.example.com http: paths: - path: / pathType: Prefix backend: service: name: edge-console port: { number: 8443 }The Deployment runs the gateway image with --platform KUBERNETES and one replica. Because the page is
served over https://, the UI derives wss://console.dallas.example.com/ws automatically — no UI config
change.
6. Single-device (no separate site broker)
Section titled “6. Single-device (no separate site broker)”On a lone device, messaging.local is that device’s local bus — the console and the device’s components
share one broker. Everything else is identical to the minimal HOST config.
{ "logging": { "level": "INFO" }, "heartbeat": { "enabled": true, "intervalSecs": 5 }, "metricEmission": { "target": "messaging" },
"messaging": { "local": { "host": "127.0.0.1", "port": 1883, "clientId": "edge-console" } },
"hierarchy": { "levels": ["device"] }, "identity": {},
"component": { "global": { "console": { "ws": { "port": 8443, "webRoot": "../ui/dist" } } }, "instances": [{ "id": "main" }] }}Give the console a distinct -t thing name so its own state/metric/cfg don’t appear as a device
under the fleet it is watching.
Field-by-field reference
Section titled “Field-by-field reference”Every knob shown above — plus its type, default, and validation rule — is documented in reference — configuration. The wire protocols the config governs are in reference — data types (browser↔console) and reference — messaging interface (console↔bus).