The engine was I/O-bound on its own state backend. `RedisState.lock()` is one key — `pipeline:_lock` — for the whole process, taken five times a message at two round trips each, and every cascade and every node read queued behind it. Inside it, reading a node's inputs was three round trips per input (an EXISTS for `in`, then EXISTS and GET for the value), writing was two updates that a single transaction already gives, and the version counters went one INCR at a time. Replaced with the atomic command that was always available: `get_present` is one MGET and tells a missing key from one holding null, so the lock it used to be read under bought nothing; value and timestamp land in one `update`, which is a MULTI/EXEC; `increment_multi` pipelines the counters. `values()` — what every websocket snapshot calls — is two reads whatever the message count instead of two per message. Beside that: every webhook did its blocking XADD on the asyncio event loop (MQTT already used `to_thread`); the per-execution `NodeOutcome` was built and validated even with no run watching; `_minute` built a tz-aware datetime per event on the loop thread to key a dict, and now keys on an int; `move_due` promoted delayed items one round trip each, every second; `FLOW_MAX_CASCADES` makes the in-flight ceiling a setting rather than a constant. `orjson` replaces stdlib json where a message pays for it — state, the journal, the engine side of the worker pipe. `fluksio-worker` stays dependency-free, and the run-cache digest stays on stdlib so no stored key is invalidated. A non-finite number now stores as `null` rather than the bare `NaN` that was never JSON. Measured with `scripts/bench_engine.py` against a real Redis, 200 messages: a five-node chain went from 43.9 to 103.1 msg/s with p50 latency 2110ms → 782ms and p95 3913ms → 1439ms; one source into twenty consumers went from 5.4 to 33.7 msg/s. In memory, twenty consumers went from 187 to 448 msg/s. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BpfSinyCBfjuieikyfMPbf
Fluksio App
Fluksio is a node-based automation software that brings trust and reliability to your flow. It just works and looks good.
The core of Fluksio: a node-based, test-driven automation software built to scale. This
repo holds the FastAPI backend, the flow engine, and the dashboard SPA. It is served on
app.${DOMAIN} (SPA) and api.${DOMAIN} (API)
Layout
backend/ FastAPI + SQLModel + Alembic + SQLite
fluksio/flow/ the flow engine (nodes, pipeline, state backends, controller)
fluksio/cli.py `fluksio serve` / `enroll` / `worker`
worker/ the `fluksio-worker` distribution: the agent and the node runner
frontend/ React 19 + TanStack Router + Tailwind 4 + shadcn/ui
docs/ the public documentation site (zensical), served on docs.${DOMAIN}
docker/ compose.yml → compose.dev.yml → compose.local.yml (+ compose.traefik.yml)
scripts/ generate-client.sh, test.sh
Install without Docker
Get started quickly by running
pip install fluksio
fluksio serve
Then, head over to fluksio.com, sign up and add a new installation. Using the code provided, run
fluksio enroll <code>
and you're ready to rock.
Fluksio is distributed at it's heart. A machine that should only run nodes for an engine elsewhere installs less:
pip install fluksio-worker
fluksio-worker --url wss://api.example.com/api/v1/workers/attach --token "$TOKEN" --labels gpu
Getting started
Normally driven from the workspace root (make init once, then make dev). Standalone:
make install # uv sync + bun install
make dev-utils # proxy and mailcatcher only
make dev-backend # FastAPI on :8000, hot reload
make dev-frontend # Vite on :5173
make test # pytest + Playwright (the e2e half needs the stack up)
make lint # ruff + mypy + biome
make generate-client # regenerate the frontend SDK from the OpenAPI schema
make help lists every target.
Documentation
The public site lives in docs/ and is built with zensical:
make docs-serve # live preview on :8000
make docs # static build into ./site
It is served at docs.${DOMAIN} by the docs service in docker/compose.yml,
and .gitea/workflows/docs.yml builds it with --strict on every push.
License
Copyright (C) 2026 Melvin Strobl - GNU Affero General Public License v3.0 or later. See LICENSE.