Skip to content

Recorder Tap Node

mchristegh edited this page Jul 14, 2026 · 1 revision

flight-recorder-tap — an ambient recorder. It observes messages flowing through other nodes via Node-RED's messaging hooks, without touching their wires. The flows being recorded need no modification and no redeploy; a tap on a dedicated tab can shadow your whole runtime.

Ports

Port Carries
Input Commands only — this node receives no traffic. Payload-addressed commands are accepted here as a convenience; the control node is the canonical path.
Output 1 — incidents Incidents: manual dumps, watermark hits, and automatic error dumps.
Output 2 — query Status snapshots (with tap extras — active mutes, scope counters).
Output 3 — events Everything, tagged with msg.recorderEvent, including the tap-specific muted / unmuted / muteIgnored.

Scope — what gets recorded

Selection is a simple algebra: (flows ∪ nodes ∪ types) − exclusions.

Field Meaning
All flows Record every node in the runtime. Overrides the three selectors below.
Flows Flow (tab) ids, comma-separated. The clickable list under the field shows every workspace by label — click to add or remove, no need to know ids.
Nodes Individual node ids.
Types Node types, e.g. mqtt in, http request — powerful for cross-cutting concerns like "record every external I/O point".
Exclude nodes / types Always win over any selector — e.g. "the whole Production tab, except that chatty poll loop".

Recorders never record recorders. All four flight-recorder node types are hard-excluded from every scope, unconditionally — even under All flows, even if explicitly named. This prevents feedback loops (a tap capturing its own incident emissions) by construction.

Scope membership is evaluated lazily per source node and cached, so a node added to a watched flow by a partial deploy is picked up the first time it sends — no tap redeploy needed. One known edge: a node moved between flows keeps its cached verdict until the tap itself is redeployed.

Subflows: nodes inside a subflow instance report types like subflow:<subflow-id>, so type-based scoping of subflow contents is unintuitive — scope by flow or node id instead.

What the tap can and cannot see

Hooks observe messages between nodes: each record is a message some node sent. The tap cannot see inside a function node's logic, nor messages a node swallows without sending — though a swallowed message shows up honestly as a gap in that source's cadence. A message fanning out to several wires is recorded once (deduplicated per emission), with the emitting node and its output port in the record's source.

Automatic error dumps

With auto-dump on errors enabled, an error reported by any in-scope node triggers dump("error", …) — the incident's context carries the error (structured, stack capped), the erroring node, and the message it was handling. The cooldown applies, so an error loop can't flood your outputs; each suppressed dump emits incidentSuppressed.

Honesty notes: this relies on nodes reporting errors properly (node.error(err, msg)) — well-behaved nodes do, some older contrib nodes swallow errors. And a hard runtime crash fires no hook at all; persistence is the recovery story for that case, which is precisely why the feature exists.

Muting — silence a channel while the tape rolls

Scope membership is static configuration; muting is the runtime knob. mute / unmute commands (with a target: { "node": "<id>" }, { "type": "mqtt in" }, or { "flow": "<tab id>" }) silence one channel while everything else keeps recording — for when a flapping sensor is drowning the buffer and you need to see everything else.

A muted gap is never invisible:

  • Suppressed messages are counted per target; the counts appear in query results and the unmuted event.
  • Muting and unmuting each lay an annotation mark on the tape, in sequence position — an incident spanning a mute window shows exactly where the gap begins and ends.
  • A muted source's errors still auto-dump. You muted its chatter, not its failures.

Mutes are deliberately ephemeral: they reset on deploy and restart (a mute silently outliving a restart is a trap). Permanent silences belong in the exclusion config. A mute targeting a node the tap has already seen and rejected as out-of-scope is refused with a muteIgnored event rather than silently accepted.

Other configuration

Capacity, capture mode, max bytes, redaction, cooldown, persistence, and the watermark all behave exactly as on the inline recorder — one shared engine, two capture modes. The watermark is evaluated against every in-scope message the tap captures.

Status

Like the inline node's, plus an active-mute count: ● 87/100 (2 muted).

Performance honesty

Every in-scope message is cloned and encoded. For most flows this is negligible; for a genuinely chatty source (a high-frequency MQTT firehose), prefer summary capture mode, keep Max bytes modest, exclude or mute the loudest sources, and remember this is a diagnostic tool, not a message historian. Two taps with overlapping scopes each keep their own buffer — memory doubles. With persistence on under sustained traffic, the write debounce means regular disk writes; on flash-based hosts (Raspberry Pi, Home Assistant Yellow eMMC) that's worth knowing.

Clone this wiki locally