Skip to content

ashaffah/flow-engine

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:
    Node Role
    inject source: emit once / periodically (timestamp/string/number/json)
    debug sink: show the payload in the debug panel
    catch / status emit a message on any node error / status change
    link-in / link-out virtual wires across the flow (by name)
    function transform the payload via rhai / JavaScript (boa) / WASM (wasmi: C/C++/Rust/Go)
    switch route a message to one of two outputs by a condition
    change set / delete / move a message property
    range scale / clamp / wrap a numeric property
    template render a {{ path }} template into a property
    filter report-by-exception: pass on only when the value changed
    delay delay each message, or rate-limit the stream
    trigger pass a message, then send another after a delay
    split / join break a message into parts / reassemble them
    sort / batch sort an array / group messages into arrays
    modbus-read master: read holding/input/coils/discrete → emit
    modbus-write master: write a register/coil, or a 32-bit float/uint over two registers, from the payload
    modbus-slave server: act as a Modbus device polled by an external master
    sysinfo source: host CPU%, memory, and local IP (the resource snapshot)
    serial-ascii read ASCII lines from a serial port (e.g. a scale) + write commands; regex, byte-map, parity/stop/data bits
    mqtt-in / mqtt-out subscribe / 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/dist
cd 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 :3000
cd ui && npm run dev      # UI on :5173, proxies /api → :3000

Configuration (env)

Env Default Description
FLOW_BIND 0.0.0.0:3000 HTTP bind address
FLOW_DB sqlite://flow-engine.db SQLite URL
FLOW_USER admin login username
FLOW_PASS admin login password
RUST_LOG info tracing 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

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors