Skip to content

Repository files navigation

flow-engine

English | Bahasa Indonesia

A Node-RED-style visual flow editor + runtime, written in Rust. A single binary: an axum server serves the UI (Svelte 5 + @xyflow/svelte, embedded), the REST API, the SSE debug stream, and runs the node execution engine in the same process. Flows are stored in SQLite and can be exported/imported as JSON.

Built with tokio-modbus, rumqttc and regex for the device I/O, and axum + rust-embed + Svelte/xyflow for the editor.

Features

  • Canvas editor drag-and-drop (xyflow): node palette, wiring, per-node config inspector, live debug panel.
  • Runtime: the Deploy button runs the graph as interconnected tokio tasks wired by channels — messages (msg) flow along the wires.
  • Built-in nodes:
    NodeRole
    injectsource: emit once / periodically (timestamp/string/number/json)
    debugsink: show the payload in the debug panel
    catch / statusemit a message on any node error / status change
    link-in / link-outvirtual wires across the flow (by name)
    functiontransform the payload via rhai / JavaScript (boa) / WASM (wasmi: C/C++/Rust/Go)
    switchroute a message to one of two outputs by a condition
    changeset / delete / move a message property
    rangescale / clamp / wrap a numeric property
    templaterender a {{ path }} template into a property
    filterreport-by-exception: pass on only when the value changed
    delaydelay each message, or rate-limit the stream
    triggerpass a message, then send another after a delay
    split / joinbreak a message into parts / reassemble them
    sort / batchsort an array / group messages into arrays
    modbus-readmaster: read holding/input/coils/discrete → emit
    modbus-writemaster: write a register/coil, or a 32-bit float/uint over two registers, from the payload
    modbus-slaveserver: act as a Modbus device polled by an external master
    sysinfosource: host CPU%, memory, and local IP (the resource snapshot)
    serial-asciiread ASCII lines from a serial port (e.g. a scale) + write commands; regex, byte-map, parity/stop/data bits
    mqtt-in / mqtt-outsubscribe / publish MQTT
  • Modbus transports: TCP, RTU-over-TCP, RTU-serial (master); TCP + RTU-serial (slave/server).
  • MQTT: protocol 3.1.1 or 5. Transports: TCP, TLS, WebSocket (ws), and secure WebSocket (wss). TLS/WSS use the system root certificates, or a custom CA / client certificate (PEM) for self-signed brokers and mutual TLS. Optional broker username/password. Configurable QoS (0/1/2), publish retain flag, and keep-alive. Optional will / birth / close messages (close is published on a graceful redeploy or shutdown). Nodes sharing a client_id reuse one connection.
  • Auth (Node-RED-style): username/password sign in (in-memory session token).
  • Localization: English (default) + Indonesian, switchable in the toolbar. The message log (node status) is localized too: the backend emits stable status codes and the UI translates them to the active language.
  • Export/Import flows as JSON ({ name, graph }).

Stack

  • Backend: Rust, axum 0.8, tokio, sqlx (SQLite), rust-embed.
  • Node runtime: tokio-modbus 0.17, tokio-serial, rumqttc, rhai, regex.
  • Frontend: Svelte 5, @xyflow/svelte, Vite.

Build & run

The UI is embedded from ui/dist, so build the UI before building the binary:

# 1. Build the UI → ui/distcd ui && npm install && npm run build &&cd ..
# 2. Build + run the binary (embeds ui/dist)
cargo run

Open http://localhost:3000. Default login admin / admin — set FLOW_USER / FLOW_PASS for any non-local deployment (the server warns when the password is left at the default). Repeated failed logins are temporarily rate-limited.

Dev (UI hot reload)

cargo run # backend on :3000cd ui && npm run dev # UI on :5173, proxies /api → :3000

Configuration (env)

EnvDefaultDescription
FLOW_BIND0.0.0.0:3000HTTP bind address
FLOW_DBsqlite://flow-engine.dbSQLite URL
FLOW_USERadminlogin username
FLOW_PASSadminlogin password
RUST_LOGinfotracing filter

API

All /api/* require Authorization: Bearer <token> except /api/login. SSE takes the token via ?token= (EventSource cannot set headers).

  • POST /api/login · POST /api/logout
  • GET /api/nodes — node type catalog (palette + inspector fields)
  • GET/POST /api/flows · GET/PUT/DELETE /api/flows/{id}
  • GET /api/flows/{id}/export · POST /api/flows/import
  • POST /api/flows/{id}/deploy · POST /api/runtime/stop · GET /api/runtime/status
  • GET /api/debug/stream — SSE: debug output + node status

Flow JSON format

{
"name": "example",
"graph": {
"nodes": [
{
"id": "n1",
"type": "inject",
"position": { "x": 0, "y": 0 },
"config": { "mode": "interval", "interval_ms": 1000 }
}
],
"edges": [{ "id": "e1", "source": "n1", "target": "n2" }]
}
}

The schema mirrors xyflow; it is not byte-compatible with a real Node-RED flows.json.

Notes

  • The function node has a Language dropdown:
    • rhai (default) / js (boa) — a script that sees payload and topic, mutates them, and the node emits the result. Example: payload = payload * 2;
    • wasm (wasmi) — upload a compiled .wasm module (C/C++/Rust/Go), run sandboxed. ABI + a C example in examples/.
  • Only one flow is active at a time; Deploy replaces the running graph.
  • Modbus slave: holds an in-memory register/coil map; external master writes update the map and emit a { fc, address, value } message on the node output. Feed messages into the node to update the map the master reads.

About

A Node-RED-style flow editor + runtime in Rust. Single binary: visual canvas, Modbus/MQTT/serial nodes, rhai/JS/WASM functions, SQLite, embedded Svelte/xyflow UI.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages