Skip to content
GitHub

HotLoop Flow 2.1.0 release notes

HotLoop Flow 2.1.0 is Phases 0 to 2 of the roadmap plus the everyday gaps that kept people from putting a real flow on it. Every deploy is now a record you can diff, test, review and put back. A flow can have tests that run before a bad deploy happens. A save restarts the nodes you changed and leaves the rest of the plant alone.

Nothing in this release changes how a flow file is read or written. The byte-exact round trip is untouched, so a flows.json that ran on 2.0.5 runs on 2.1.0.

Deploys

  • Every deploy is a record: who, from where, when, the note, the revision it replaced, the flow file byte for byte, and the credentials encrypted as they stood. GET /deployments reads them. history.retain keeps the last 100 by default.
  • A diff that reads like a review: node by node, property by property, layout kept apart from logic, credentials never shown. In the API, on the command line (hotloop-flow diff) and as git’s own diff driver.
  • Rollback puts an old record back as a new deploy, flows and credentials together, so a deleted node comes back with its broker password.
  • Partial deploy: a save restarts the nodes that changed. MQTT sessions, listeners and Delay queues on everything else keep running. HotLoop-Flow-Deployment-Type (or Node-RED’s header) picks nodes, flows or full.
  • The editor reviews before it deploys, with the diff drawn on the canvas and a note, and shows the history with a rollback button.
  • Flows as code: hotloop-flow deploy prints the diff against what’s running and deploys against the revision it came from, so a race is a red pipeline. hotloop-flow export hands back the live file byte for byte. hotloop-flow token makes an API token and the hash to configure for it.
  • Optional git mirror: with history.git set, every deployment is a commit authored by whoever deployed it.

Tests

  • Flow tests: flows.test.yaml next to flows.json, run by hotloop-flow test (JUnit with -junit) on the real runtime and the real nodes. Delay, Trigger, Inject, Join and Batch run on a clock the test moves, so five minutes of Delay is one step. Anything that talks to the outside world, MQTT, HTTP, PostgreSQL, InfluxDB, TCP, UDP, WebSocket, files, exec, is stood in for, and the test checks what would have gone out.
  • The deploy gate: turn on tests.gate and a deploy whose flows fail the suite is a 422 with the whole report. Rollbacks aren’t gated, because a rollback is the way out.
  • The editor makes a test out of a debug message and runs it.

Nodes and protocols

  • JSONata, proven against the published jsonata-js 2.2.2 suite: 1673 of 1686 cases pass, and the 13 that don’t are pinned by name with the reason.
  • Inject fires on a crontab, on the same minute Node-RED would, with the clock changes in six zones checked.
  • MQTT 5 (properties, subscription options, session expiry) and the last will. MQTT 3.1.1 still works.
  • TLS on TCP, HTTP request and MQTT through a tls-config node. The WebSocket listener and client start, and the client does TLS.
  • Cookies and multipart on HTTP, checked against the libraries Node-RED uses.
  • Split, Join and Batch work the way Node-RED’s do: streaming, timeouts, reduce, intervals. Link Call, and Link Out’s return mode.
  • A password can live in a mounted Secret instead of the flow. The Helm chart gets secrets.mounts.

Signing in

  • Two-factor sign-in (TOTP), free, for anybody who turns it on from the editor.
  • Lockouts, sessions that survive a restart, an audit log of who did what from where (GET /audit, audit.read).
  • Lockout is on by default: 5 failures for one account from one address, or 20 from one address, locks it for 15 minutes with a 429. auth.lockout changes the numbers.
  • Sessions are kept in data/sessions.json as hashes and follow the config on every request, so taking a permission or a user out of the config takes effect on the next request.
  • A rollback is a full restart unless you ask for less.
  • The image carries LICENSE, NOTICE and THIRD_PARTY_LICENSES in /usr/share/doc/hotloop-flow/. THIRD_PARTY_LICENSES lists every Go module and npm package in the binary with its version and license text, generated at build time. The build fails on a GPL, AGPL or LGPL dependency or one nobody can identify.
  • NOTICE was wrong. It said no Node-RED code was in here. Cron, Split, Join, Batch, Link Call, tls-config and the HTTP cookie and multipart work are ports, and it now lists every ported file with where it came from, who holds the copyright and the license, with the full ISC text for cronosjs and the MIT text for the rest. Every ported file says the same at the top.
  • A refused deploy no longer prunes credentials in memory, and double-clicking a node in Chromium opens its dialog. Both were found while building this.

From 2.0.5 it’s a plain image bump (helm upgrade, or the new tag in your Quadlet unit). No migration, no flag.

  • The first start creates sessions.json, audit.log and a deployments directory under data.dir. Sessions were in memory before, so everyone signs in once after the upgrade.
  • Everything new in the config is optional and off or harmless by default. Nothing you have stops working.
  • WebSocket tokens: the editor now sends its token as a subprotocol, hotloop-flow.bearer.<token>, offered beside hotloop-flow. That’s the preferred way for anything that connects to /comms. A script that sends ?access_token= in the URL still works, and the server logs a warning for every such connection saying that it leaks into proxy and access logs and what to send instead. The URL itself is never written to Flow’s log or the audit trail. Removing the URL form waits for a future major release.
YAML Environment Default
tests.gate HOTLOOP_FLOW_TESTS_GATE false. true refuses a deploy whose flows fail flows.test.yaml.
tests.timeout 2m, the wall clock for one run of the tests. A run that can’t finish refuses the deploy.
secrets.allowedPaths HOTLOOP_FLOW_SECRET_ALLOWED_PATHS empty. Extra trees a tls-config or a credential file may be read from, on top of data.dir. Not the file nodes’ scope: a File In node still can’t read them.
secrets.mounts (chart) empty. Kubernetes Secrets mounted read-only, each added to the secret scope.
auth.tokens HOTLOOP_FLOW_DEPLOY_TOKEN_HASH none. API tokens for machines, stored as hashes. The environment variable makes a deploy token that can read and deploy and nothing else. Make one with hotloop-flow token.
auth.lockout attempts: 5, perAddress: 20, window: 15m, duration: 15m
history.retain 100. 0 keeps every record.
history.git HOTLOOP_FLOW_GIT_URL, HOTLOOP_FLOW_GIT_USERNAME, HOTLOOP_FLOW_GIT_PASSWORD off.
audit.maxBytes, audit.keep 16 MiB, 4 files.

It has never run on a real plant floor. The tests, the race detector, a real Mosquitto and the browser tests are all behind it, and none of that is a plant. Nobody has clicked through the HTTP, WebSocket, TCP and UDP dialogs by hand.

Image: ghcr.io/hotloop-io/hotloop-flow:2.1.0, linux/amd64 and linux/arm64. Chart: https://hotloop.io/hotloop-flow/. The README at this tag has the rest: https://github.com/HotLoop-io/hotloop-flow/blob/v2.1.0/README.md