Releases: RAAS-Impact/n8n-uno-q
v0.4.0 — Reverse-SSH transport + close-path robustness
Lockstep release of @raasimpact/arduino-uno-q-bridge@0.4.0 and n8n-nodes-uno-q@0.4.0. Adds a fourth transport — reverse-SSH (Variant B) — for NAT-ed Qs that can dial out but can't accept inbound connections. Ships alongside a robustness fix in Bridge.close() that prevents MCU code from hanging when an n8n bridge closes with router-forwarded requests in flight.
The Q-side autossh container ships separately under deploy/relay-ssh/ (deploy tooling shipped 2026-04-25, n8n-side this release).
@raasimpact/arduino-uno-q-bridge 0.3.0 → 0.4.0
Added
SshTransport— fourth transport, sitting on top of an externally-suppliedDuplexstream rather than dialing a socket itself. The Variant B deployment routes each device through the n8n-side singleton (master plan §14); the bridge just receives theforwardOutchannel for one specific device. Construct withnew SshTransport({ connect: () => Promise<Duplex> })and pass it viaBridge.connect({ transportInstance }).'ssh'discriminant onTransportDescriptorwithlistenAddress,listenPort,deviceNick(the cert KeyID — the only routing key on the n8n side per §14.4). The factory throws if a caller tries to construct an SSH transport from descriptor alone — theDuplexmust come from the singleton, sotransportInstanceis mandatory for this kind.
Changed
Bridge.close()now drains in-flight router-forwarded requests before tearing down the transport. Each pendingprovidehandler invocation gets an explicit[1, "bridge closing while handling <method>"]error response written to the wire, so any caller blocked on a synchronous reply (notably an MCU executingBridge.call(...)inside itsloop()) unblocks instead of hanging forever. The handler's own response writes use the in-flight Map'sdeleteas a CAS guard to avoid double-sends.Bridge.close()now sends\$/resetto the router (with a 500 ms cap so a slow router doesn't block close) to drop every method this connection registered. Without this, the router kept routing for a dead socket; the next caller for one of those methods would either hang or surface a transport-layer error rather than a clean method not available. Sent unconditionally because callers (tests, drift recovery paths) may have mutatedprovidersdirectly, so the local view ≠ router-side truth.
n8n-nodes-uno-q 0.3.0 → 0.4.0
Added
- Reverse-SSH transport mode on the
Arduino UNO Q Routercredential. Selectable from the same dropdown as the Unix / TCP / mTLS variants. Configuration: a listen address + port the n8n side binds, the host private key the embedded SSH server presents, the user CA public key it trusts, and a Required principal (defaults totunnel). Routing is by user-cert KeyID — the only n8n-side routing key per master plan §14.4. - Process-singleton
SshServerthat accepts inbound connections from Q autossh clients, validates user certs against a single CA, evicts zombie reconnects when a new client claims the same KeyID, and exposesconnect(deviceNick)to nodes — opens aforwarded-tcpipchannel back through the SSH session and returns theDuplexforSshTransportto wrap. LikeBridgeManager, stashes itself onglobalThisunder aSymbol.for(...)key so each esbuild bundle of a node file shares the same instance at process scope. sshCertParser— manual OpenSSH user-cert parser + ed25519 signature verification, written because ssh2 v1.17 doesn't surface user certs through itsparseKeyAPI. Validates KeyID, principals, validity window, extensions (permit-port-forwardingrequired), and rejects any unrecognised critical option per the OpenSSH spec.- Test Connection for the SSH transport runs the standard
\$/versionround-trip end-to-end through the spawned forward, surfacing transport-specific failures (no device registered, cert rejected, host key mismatch) as legible messages at credential save rather than at first execution. BridgeManagerconnection-pool entry for the'ssh'descriptor kind — keyed by(listenAddress, listenPort, deviceNick)so the same Q reached from two nodes shares one Bridge, while two Qs on the same listener share the listener but get separate Bridges. TheSshServeris brought up lazily when the first SSH-mode credential acquires, and torn down when the last reference drops.
Changed
- Bumped
@raasimpact/arduino-uno-q-bridgeto^0.4.0— required forSshTransport. The same bump brings in the close-time drain +\$/resetbehaviour that prevents MCU code from hanging when a bridge closes with router-forwarded requests in flight (relevant whenever a workflow saves, an agent deactivates, or n8n restarts mid-call).
This release was validated against a real UNO Q across all four transport variants (unix, tcp, mtls, ssh) plus the arduino-cloud suite — 56/56 integration assertions pass, and a post-suite probe confirms the MCU's loop() stays unwedged, which it didn't reliably before the close-path fix. The same orchestrator (./scripts/run-integration.sh) is now part of the repo for future releases.
v0.3.0 — Multi-Q + multi-transport
Lockstep release of @raasimpact/arduino-uno-q-bridge@0.3.0 and n8n-nodes-uno-q@0.3.0. A single n8n instance can now drive several UNO Qs (locally and remotely) by assigning a different Arduino UNO Q Router credential to each node, including remote Qs reached over plain TCP or mTLS via the relay containers shipped under deploy/relay/ and deploy/relay-mtls/.
@raasimpact/arduino-uno-q-bridge 0.2.0 → 0.3.0
Added
- Plain TCP transport —
Bridge.connect({ host, port })for use with the Variant Asocatrelay on a trusted LAN. Same wire protocol as the Unix socket, just over a TCP stream. - mTLS transport —
Bridge.connect({ host, port, tls: { ca, cert, key } })for the Variant Cstunnelrelay. The bridge presents a client certificate and validates the server cert against the supplied CA. PEM strings inline; no filesystem path required. TransportDescriptordiscriminated union ({ kind: 'unix' | 'tcp' | 'tls', ... }) — what the newconnect()overloads compile down to internally and what consumers liken8n-nodes-uno-q's credential resolver round-trip through configuration.disconnectevent reason payload — surfaces why the socket dropped so consumers can distinguish a clean close from a network failure or a timeout.
Changed
- The single-file
transport.tswas split into atransport/directory (unix-socket.ts,tcp.ts,tls.ts, plus asocket-base.tsmixin and afactory.tsfor descriptor → transport instantiation). Behaviour preserved for existing Unix-socket callers; the new structure is what made TCP/TLS additions clean.
Fixed
[code, message]error tuple format — Arduino'sArduino_RouterBridgelibrary returns errors as a[code, message]array; the bridge now decodes both formats so msgpack-RPC errors raised by the MCU surface readably instead of as opaque arrays.- Requests for unregistered methods now respond with a structured error rather than dropping silently.
n8n-nodes-uno-q 0.2.1 → 0.3.0
Added
Arduino UNO Q Routercredential type — replaces the implicit per-package socket path with a per-node credential. Three transport modes:- Unix Socket (local) — same-host, default
/var/run/arduino-router.sock. - TCP (plain) — Variant A relay, trusted LAN.
- TCP + mTLS — Variant C relay, untrusted networks. Three PEM fields (CA, client cert, client key) when Use TLS is on; key encrypted at rest.
- Test Connection runs
\$/versionover the configured transport and surfaces transport-specific failures (socket not found, TLS handshake, connection refused, …) instead of a generic error.
- Unix Socket (local) — same-host, default
- Multi-Q workflows — assign different credentials to different nodes in the same workflow to read sensors on one Q and fire actuators on another. The
BridgeManagersingleton now keys its connection pool by transport descriptor, refcounts subscriptions per(descriptor, method), and tears each connection down independently when its subscriber count hits zero. transport-resolvermodule that turns a credential payload into aTransportDescriptor, with explicit validation for the mTLS PEM trio.- Diagnostic snapshots in
BridgeManager(entries-by-descriptor, refcounts, pending close state) for troubleshooting connection-pool issues against a remote Q.
Changed
- All four nodes (
Arduino UNO Q Call,Arduino UNO Q Trigger,Arduino UNO Q Respond,Arduino UNO Q Method) now require a credential assignment. Workflows configured before 0.3.0 will need a one-time credential creation per Q. - Bumped
@raasimpact/arduino-uno-q-bridgepeer dependency to^0.3.0— required for the new TCP / mTLS transports.
Fixed
BridgeManagerrefcount leaks — refcounts now restore on every error path, includingprovideregistrations that fail mid-handshake.- Request-mode single-owner invariant — a Trigger node in synchronous response mode is the only owner of its method's bridge subscription; a second node trying to claim the same method now errors at registration instead of silently sharing the channel.
- Credential UI masking — TLS PEM fields render as multi-line text (not password-masked single-line), preserving whitespace exactly so the PEM parser sees what the user pasted.
Companion release
A separate, brand-new package shipped alongside this one — same monorepo, disjoint runtime: n8n-nodes-arduino-cloud@0.1.0, tagged at n8n-nodes-arduino-cloud-v0.1.0. Read / write Arduino Cloud Thing Properties, react to property updates over MQTT, and expose properties as AI Agent tools — for any Arduino Cloud-connected board (Nano 33 IoT, MKR WiFi 1010, Portenta, UNO R4 WiFi, Nano ESP32, …), not just the UNO Q.
v0.2.1 — Rate limiting and budget-aware guards
Package: n8n-nodes-uno-q@0.2.1 (bridge unchanged at 0.2.0)
Added
- UnoQTool — Rate Limit collection (
Max Calls+Perof minute / hour / day). Caps how often the AI Agent may invoke the tool; excess calls short-circuit with a structured rejection{ refused: true, error: "Refused: rate limit of N per <window> exceeded. Retry in ~Xs." }that the LLM reads and can react to. The check runs before the Method Guard, and the call is recorded only after both gates pass — so guard-rejected calls do not consume rate-limit budget. Counters are a sliding window kept in-memory per n8n process (reset on container restart, not shared across queue-mode workers, which remains unsupported for the same reason the bridge singleton is). budgetvariable in Method Guard scope — a read-only view of the call history for traffic-aware policies.budget.used(window)returns prior successful calls in the last'minute' | 'hour' | 'day'and works whether or not a Rate Limit is configured, so guards can implement soft caps without committing to hard enforcement.budget.remainingandbudget.resetsInMsexpose cap-aware state when the Rate Limit field is set (number / number) and arenullotherwise — enabling patterns like priority reservation ("refuse low-priority params whenremaining < 3"). Exposed vianew Function('method', 'params', 'budget', <guard body>).
See packages/n8n-nodes/README.md#rate-limit-and-budget for the full contract and three worked patterns.
v0.2.0 — Method guard + retry
Capability-metadata release across both packages: the bridge now retries idempotent calls through cascading socket drops, and UnoQTool gains a user-defined JavaScript guard that vets each LLM invocation at the gate.
@raasimpact/arduino-uno-q-bridge@0.2.0
Added
Bridge.callWithOptions(method, params[], opts)— new public entry point taking{ timeoutMs, idempotent }. Whenidempotent: true, in-flight calls that hitConnectionErrorrace the bridge'sreconnectevent against the remainingtimeoutMsbudget and retry — repeatedly, through cascading drop/reconnect cycles, until the call resolves or the budget runs out. Never retries onTimeoutErroror non-idempotent calls. Calls that start against a known-disconnected bridge fast-fail withConnectionErrorinstead of waiting out the timer.
Changed
call()andcallWithTimeout()are now thin wrappers overcallWithOptionswithidempotent: false. Behavior preserved for existing callers.
Full changelog: packages/bridge/CHANGELOG.md
n8n-nodes-uno-q@0.2.0
Added
- UnoQTool — Method Guard field. Optional JavaScript body that runs at invocation time with
methodandparamsin scope and decides whether the call proceeds. Typical uses: validating LLM-supplied arguments, time-of-day gating, external-state checks. Return a string to reject — it's surfaced as structured tool output so the AI Agent feeds the message back to the LLM for self-correction. Uses n8n'sjsEditorwidget. - UnoQTool — Idempotent top-level checkbox driving the bridge's retry on mid-call
ConnectionError.
Changed (minor breaking)
- UnoQCall — Idempotent moved from Options collection to a top-level checkbox for consistency with UnoQTool. Workflows saved with
options.idempotent: truepre-0.2.0 silently reset to defaultfalseon load; re-tick at the top level. No runtime error — just quiet loss of the setting.
Removed
safeReadOnlyboolean (advisory, never enforced). Replaced by Method Guard, which vets each invocation rather than signalling a blanket per-method claim.
Full changelog: packages/n8n-nodes/CHANGELOG.md
All existing unit tests pass (26); bridge integration suite against a real UNO Q also passes (10/10). The method-guard path has been manually verified in a live n8n workflow — guard rejections reach the LLM as observations rather than as workflow errors.
v0.1.1 — Bridge hygiene
Bridge-hygiene maintenance release covering both packages.
@raasimpact/arduino-uno-q-bridge@0.1.1
Added
disconnectevent emitted byBridgewhenever the transport socket drops. Consumers now have an explicit hook for cleaning up deferred state instead of waiting for a response that will never arrive.
Fixed
- In-flight
providehandlers are cleared fromactiveHandlerswhen the socket closes mid-call. Previously orphaned handlers lingered in tracking after a mid-call drop, surviving reconnects and causing shutdown-drain paths to wait on handlers whose eventual RESPONSE had nowhere to land.
Full changelog: packages/bridge/CHANGELOG.md
n8n-nodes-uno-q@0.1.1
Fixed
BridgeManagerserializesacquire()andgetBridge()after a priorrelease()by awaiting the background close. Rapid deactivate / reactivate cycles on the same method previously left two connections briefly overlapping on the router, causing the new$/registerto be rejected with a "method already registered" error on workflow reactivation.
Changed
- Bumps dependency on
@raasimpact/arduino-uno-q-bridgeto^0.1.1to pick up the bridge's newdisconnectevent and orphan-handler cleanup on socket close.
Full changelog: packages/n8n-nodes/CHANGELOG.md
All existing tests remain green; three new unit tests guard the fixed behaviours (two in the bridge package, one in n8n-nodes). Integration tests run against a real UNO Q also pass.