The Gateway
"The gateway" is the QUBIQ installation as a whole: the supervisor process, the services it manages, and the message bus they share. It is the unit you install, start, stop, back up and upgrade.
The supervisor
Running the binary with no subcommand starts the supervisor. In order, it:
- Provisions
.envif absent, generatingJWT_SECRETand a 64-hexENCRYPTION_KEYfrom a cryptographic RNG. - Migrates the configuration database — once, single-threaded, before any child exists, so services never race on schema.
- Starts the embedded message bus, bound to
127.0.0.1on a random free port, and exports the resulting URL into the environment children inherit. Its durable store — every store-and-forward buffer, the quarantine and shared runtime state — isnats_data/in the data directory 1.0.8. After an unclean stop the bus rebuilds that store before it accepts connections; the supervisor waits for it, up to 15 minutes, logging progress every 10 seconds 1.0.8. - Sets aside damaged bus state 1.0.8. A consumer whose state file was
corrupted by a forced stop cannot be recreated or deleted from a running service, and used to
crash-loop the service that owns it. The supervisor moves such directories into
nats_data/corrupt-consumers/before any service starts, and the service recreates the consumer cleanly. Nothing is deleted, and no unprocessed message is lost. - Creates one hard link per service in a
servicesfolder beside the binary, namedQBQ-<Service>, and spawns each. - Supervises: watches heartbeats, restarts a service that exits unexpectedly, and reports status to the interface.
If hard links are not possible (a cross-volume or non-NTFS install), the supervisor falls back to
spawning qubiq <subcommand> — still a separate process, just displayed under the generic name.
So that QBQ-OpcWorker shows up in Task Manager, ps and your monitoring agent under its own name.
It is the same file on disk; only the invocation name differs.
On Windows, Task Manager names a process by the description stored inside the program file rather
than by its file name, so in earlier builds every service appeared as the same QUBIQ entry. Since
1.0.8 1.0.8 the supervisor writes one copy of the binary,
services\qubiq-service.exe, with that description removed, and links the services to it — so each
appears under its own QBQ-… name. The copy costs one binary's worth of disk, is refreshed only when
qubiq.exe changes, and is unsigned (removing the description would otherwise leave a broken
signature); qubiq.exe itself keeps its signature. If the copy cannot be made, the services link to
qubiq.exe and run as before.
As a Windows service
Installed as a Windows service, the supervisor now reports to the Service Control Manager first and then does its whole boot — migration, licence, bus restore — reporting start pending with progress every 10 seconds, and running once the fleet is up. 1.0.8 Windows gives a service about 30 seconds to check in. Earlier builds did the boot before checking in, so a start that had a large store-and-forward backlog to restore could be failed by Windows exactly when the plant most needed it back. A boot that fails is reported as a service failure, so the service's recovery actions apply.
Logs the supervisor keeps New in 1.0.8
Each service's output is written to log/services/QBQ-<Service>.log in the data directory, and the
supervisor's own — including the bus's warnings — to QBQ-Supervisor.log beside them. They are
bounded so a noisy or crash-looping service cannot fill the disk.
→ Per-service logs
Secrets are strict
There are no insecure defaults and no fallbacks:
| Secret | Purpose | Rule |
|---|---|---|
JWT_SECRET | Protects Platform sign-ins | Required. Absent or malformed ⇒ hard startup failure. |
ENCRYPTION_KEY | 64 hex chars; derives the key protecting the configuration database and connection secrets | Required. Absent or malformed ⇒ hard startup failure. |
LIVE_JWT_SECRET | Protects LiveView sign-ins | Derived from JWT_SECRET if unset; set it explicitly to reset operator sign-ins on their own. |
Startup fails loudly rather than starting insecurely. Moving a data directory to another machine
requires bringing that machine's ENCRYPTION_KEY with it.
Service catalog
| Service | Process | Port | Restart impact |
|---|---|---|---|
| CoreServer | QBQ-CoreServer | 8182 | UI and API unavailable while down; devices keep being polled. |
| RealtimeGateway | QBQ-RealtimeGateway | 8183 | Screens stop updating; values are not lost. |
| Engine | QBQ-Engine | — | Pipeline execution pauses. |
| Bridge | QBQ-Bridge | — | Protocol adapters and pipeline runtime restart; subscriptions re-establish. |
| Historian | QBQ-Historian | — | Ingestion buffers; nothing is lost while store-and-forward holds. |
| Alarm | QBQ-Alarm | — | Evaluation pauses; state is restored from storage on restart. |
| Audit | QBQ-Audit | — | Events stay buffered on the durable stream until it returns. |
| ConnMonitor | QBQ-ConnMonitor | — | Health indicators go stale; data flow is unaffected. |
| OpcWorker | QBQ-OpcWorker | — | OPC-UA subscriptions re-establish on restart. |
| MqttWorker | QBQ-MqttWorker | — | Broker sessions reconnect; retained/Sparkplug birth messages resynchronise. |
| ModbusWorker | QBQ-ModbusWorker | — | Polling resumes. |
| EipWorker | QBQ-EipWorker | — | EtherNet/IP (Allen-Bradley CIP) polling resumes at once if the controller has reclaimed the connection slots the worker held; if it has not, the driver retries on a 500 ms-to-30 s backoff until it does. |
| S7Worker | QBQ-S7Worker | — | Siemens S7comm polling resumes the same way, but an S7 CPU releases connection resources slowly, so the retry floor is higher: a 2 s-to-30 s backoff. |
| SnmpWorker | QBQ-SnmpWorker | 162/udp (opt-in) | Polling resumes; traps sent while down are lost (SNMP is fire-and-forget). |
| TcpUdpWorker | QBQ-TcpUdpWorker | — | Sockets reconnect. |
| DbWorker | QBQ-DbWorker | — | Queued SQL/Mongo work retries. |
| RestGateway | QBQ-RestGateway | 8090 | Published REST endpoints return errors while down. |
| ScriptGateway | QBQ-ScriptGateway | 8091 | Script execution fails fast. |
| ImportService | QBQ-ImportService | — | Bulk imports queue. |
| Expression | QBQ-Expression | — | Expression tags stop updating; device and internal tags are unaffected. State is recovered on restart. |
| Knowledge | QBQ-Knowledge | — | The assistant's knowledge base is unavailable; the assistant and the plant keep running. |
| AI | QBQ-AI | — | The assistant is unavailable. Extra replicas are named QBQ-AI-2, QBQ-AI-3, … |
| PyWorker | QBQ-PyWorker | — | Python scripts cannot run until it returns. Extra replicas (PYTHON_WORKER_REPLICAS) are named QBQ-PyWorker-2, … |
Monitoring and controlling services
Settings → System Services lists every service with its state and heartbeat.
| Action | Permission |
|---|---|
| View the service list | Access Settings |
| Start / stop / restart a service | Manage System Services |
Stopping a service through the interface tells the supervisor not to restart it, so it stays down until you start it again. That is the safe way to take a protocol worker out of service for maintenance — killing the process merely triggers a restart.
Health and diagnostics
| Auth | Returns |
|---|---|
| none | Liveness — is the gateway process answering? |
| none | Aggregate service health, suitable for a load balancer. |
| View Status Panel | Resource and throughput counters. |
| Access Settings | Telemetry pipeline and WebSocket fan-out health. |
| View Status Panel | Host CPU, memory, disk. |
| View Status Panel | Per-connection state. |
| authenticated | What happened during boot — the first thing to read after a failed start. |
The message bus
Services communicate over an embedded publish/subscribe bus with a persistent stream layer. It provides:
- Subject-based messaging — telemetry, node status, connection state, alarm events, audit events.
- Durable streams — the store-and-forward buffers and the audit event stream survive a service restart.
- Key-value buckets — live tag values, internal (memory) tag values, alarm runtime state.
- Request/reply — e.g. a protocol worker asking CoreServer to resolve and decrypt a connection.
Because the bus is bound to loopback with a random port, there is nothing to expose, authenticate or firewall. It also means every service must run on the same host.
Wire contract
Telemetry travels as a compact binary message rather than JSON, which is what keeps a busy screen cheap to feed. Three kinds cross the bus: a tag value with its quality and timestamps, a pipeline node's status, and the pipeline payload envelope.
Web assets
In a production build the compiled frontend is embedded into the binary. There is no static file directory to deploy and no web server to configure; CoreServer serves the interface itself.
Ports summary
| Port | Bound by | Purpose |
|---|---|---|
| 8182 | CoreServer | HTTP API, web UI, WebSocket upgrade |
| 8183 | RealtimeGateway | Telemetry WebSocket |
| 8090 | RestGateway | Pipeline-defined REST ingress |
| 8091 | ScriptGateway | Script execution |
| 162/udp | SnmpWorker | Trap listener (opt-in) |
| random, loopback | Supervisor | Message bus — never exposed |
Override with PORT, REALTIME_PORT, REST_GATEWAY_PORT, SCRIPTING_GATEWAY_PORT.
→ Environment variables
Next
→ Projects