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.
What’s new
Section titled “What’s new”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 /deploymentsreads them.history.retainkeeps 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 deployprints the diff against what’s running and deploys against the revision it came from, so a race is a red pipeline.hotloop-flow exporthands back the live file byte for byte.hotloop-flow tokenmakes an API token and the hash to configure for it. - Optional git mirror: with
history.gitset, every deployment is a commit authored by whoever deployed it.
Tests
- Flow tests:
flows.test.yamlnext toflows.json, run byhotloop-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.gateand 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-confignode. 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).
What changed
Section titled “What changed”- 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.lockoutchanges the numbers. - Sessions are kept in
data/sessions.jsonas 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,NOTICEandTHIRD_PARTY_LICENSESin/usr/share/doc/hotloop-flow/.THIRD_PARTY_LICENSESlists 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. NOTICEwas 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.
Upgrade notes
Section titled “Upgrade notes”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.logand adeploymentsdirectory underdata.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 besidehotloop-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.
New settings
Section titled “New settings”| 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. |
What isn’t proven
Section titled “What isn’t proven”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