Skip to content

Releases: RAAS-Impact/n8n-uno-q

v0.4.0 — Reverse-SSH transport + close-path robustness

26 Apr 12:06

Choose a tag to compare

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-supplied Duplex stream 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 the forwardOut channel for one specific device. Construct with new SshTransport({ connect: () => Promise<Duplex> }) and pass it via Bridge.connect({ transportInstance }).
  • 'ssh' discriminant on TransportDescriptor with listenAddress, 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 — the Duplex must come from the singleton, so transportInstance is mandatory for this kind.

Changed

  • Bridge.close() now drains in-flight router-forwarded requests before tearing down the transport. Each pending provide handler 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 executing Bridge.call(...) inside its loop()) unblocks instead of hanging forever. The handler's own response writes use the in-flight Map's delete as a CAS guard to avoid double-sends.
  • Bridge.close() now sends \$/reset to 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 mutated providers directly, 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 Router credential. 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 to tunnel). Routing is by user-cert KeyID — the only n8n-side routing key per master plan §14.4.
  • Process-singleton SshServer that 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 exposes connect(deviceNick) to nodes — opens a forwarded-tcpip channel back through the SSH session and returns the Duplex for SshTransport to wrap. Like BridgeManager, stashes itself on globalThis under a Symbol.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 its parseKey API. Validates KeyID, principals, validity window, extensions (permit-port-forwarding required), and rejects any unrecognised critical option per the OpenSSH spec.
  • Test Connection for the SSH transport runs the standard \$/version round-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.
  • BridgeManager connection-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. The SshServer is brought up lazily when the first SSH-mode credential acquires, and torn down when the last reference drops.

Changed

  • Bumped @raasimpact/arduino-uno-q-bridge to ^0.4.0 — required for SshTransport. The same bump brings in the close-time drain + \$/reset behaviour 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

25 Apr 14:27

Choose a tag to compare

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 transportBridge.connect({ host, port }) for use with the Variant A socat relay on a trusted LAN. Same wire protocol as the Unix socket, just over a TCP stream.
  • mTLS transportBridge.connect({ host, port, tls: { ca, cert, key } }) for the Variant C stunnel relay. The bridge presents a client certificate and validates the server cert against the supplied CA. PEM strings inline; no filesystem path required.
  • TransportDescriptor discriminated union ({ kind: 'unix' | 'tcp' | 'tls', ... }) — what the new connect() overloads compile down to internally and what consumers like n8n-nodes-uno-q's credential resolver round-trip through configuration.
  • disconnect event 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.ts was split into a transport/ directory (unix-socket.ts, tcp.ts, tls.ts, plus a socket-base.ts mixin and a factory.ts for 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's Arduino_RouterBridge library 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 Router credential 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 \$/version over the configured transport and surfaces transport-specific failures (socket not found, TLS handshake, connection refused, …) instead of a generic error.
  • 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 BridgeManager singleton 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-resolver module that turns a credential payload into a TransportDescriptor, 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-bridge peer dependency to ^0.3.0 — required for the new TCP / mTLS transports.

Fixed

  • BridgeManager refcount leaks — refcounts now restore on every error path, including provide registrations 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

20 Apr 20:29

Choose a tag to compare

Package: n8n-nodes-uno-q@0.2.1 (bridge unchanged at 0.2.0)

Added

  • UnoQTool — Rate Limit collection (Max Calls + Per of 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).
  • budget variable 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.remaining and budget.resetsInMs expose cap-aware state when the Rate Limit field is set (number / number) and are null otherwise — enabling patterns like priority reservation ("refuse low-priority params when remaining < 3"). Exposed via new 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

20 Apr 19:10

Choose a tag to compare

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 }. When idempotent: true, in-flight calls that hit ConnectionError race the bridge's reconnect event against the remaining timeoutMs budget and retry — repeatedly, through cascading drop/reconnect cycles, until the call resolves or the budget runs out. Never retries on TimeoutError or non-idempotent calls. Calls that start against a known-disconnected bridge fast-fail with ConnectionError instead of waiting out the timer.

Changed

  • call() and callWithTimeout() are now thin wrappers over callWithOptions with idempotent: 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 method and params in 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's jsEditor widget.
  • 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: true pre-0.2.0 silently reset to default false on load; re-tick at the top level. No runtime error — just quiet loss of the setting.

Removed

  • safeReadOnly boolean (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

20 Apr 09:31

Choose a tag to compare

Bridge-hygiene maintenance release covering both packages.

@raasimpact/arduino-uno-q-bridge@0.1.1

Added

  • disconnect event emitted by Bridge whenever 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 provide handlers are cleared from activeHandlers when 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

  • BridgeManager serializes acquire() and getBridge() after a prior release() 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 $/register to be rejected with a "method already registered" error on workflow reactivation.

Changed

  • Bumps dependency on @raasimpact/arduino-uno-q-bridge to ^0.1.1 to pick up the bridge's new disconnect event 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.