OpexFlow is built around four primitives. Everything else is plumbing.
| Primitive | Role |
|---|---|
| Source (adapter) | Ingests events from a protocol: Modbus poll, OPC-UA subscription, HTTP REST poll, MQTT topic, NATS subject. |
| Transform (per-source route) | Reshapes each event before fan-out. Keyed by {source}:{machine.id}, with a shared default. Either a DSL program or a JavaScript function. |
| Sink (fan-out) | Delivers the result: hub (WebSocket), HMAC-signed HTTP push, NATS, MQTT, SQLite, ClickHouse, Postgres. |
| Flow (canvas tab) | A UI grouping of sources, transforms, and sinks on the canvas. Layouts persist to flows.layout.json; routing stays global. |
Every event is normalized to CanonicalMachineData at the source boundary, so
transforms and sinks always see the same shape regardless of protocol.