Files
stroblmeandClaude Opus 5 bdad6d7fc2
Docs / docs (push) Successful in 37s
Playwright Tests / test-playwright (1, 2) (push) Failing after 1m35s
Playwright Tests / test-playwright (2, 2) (push) Failing after 17s
pre-commit / pre-commit (push) Failing after 2m8s
Test Backend / test-backend (push) Failing after 2m48s
Compose Smoke Test / test-compose (push) Failing after 13s
Playwright Tests / merge-reports (push) Failing after 2m25s
Make the docs state things rather than argue them
The site read as a design journal: rationale paragraphs, hedges
("deliberately", "on purpose", "genuinely"), meta-commentary about the docs
themselves, and one em-dash every ten lines carrying an aside.

Roughly twenty rationale blocks are gone or reduced to what a reader needs
in order to use the thing. Em-dashes go from 507 to 135, and what is left is
structural rather than prose: list and definition separators, table cells,
and four inside code blocks that quote what the CLI actually prints.

Also: api.example.com becomes api.fluksio.com (the emails stay, since
bootstrap.py really defaults to admin@example.com and RFC 2606 reserves it);
the mqtt table gains the two settings it had drifted behind on and inject's
wording matches the engine; llms.txt lists the two connector pages that were
in the nav but not in it; and the two device/device_policy notes now agree.

Builds clean under `zensical build --strict`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015YrQnKV3bnQd4K342y8tKj
2026-08-31 10:49:58 +02:00

11 KiB

Node types

Every type the canvas can place. Each is configured by filling in a form the editor generates from its parameter schema, so they all behave the same way.

Anything here can also be written as a Function node. These types cover the shapes that come up most often, as a form to fill in rather than repeated code.

GET /flows/node-types returns this list with each type's full parameter schema, including any connectors installed on your instance.

Function

python — your own code, run on every incoming message.

The one type with a source file. Its arguments are its input ports and its own settings; its return value is a dict keyed by output ports. See Writing node code.

Settings on a Function node are free-form: you add them by name, and they arrive as keyword arguments.

Integrations

MQTT

mqtt — subscribe to topics, or publish what arrives on its inputs.

A node with outputs only subscribes; a node with inputs publishes.

Setting Default Notes
topic * one topic for every port, or {"message": "some/topic"} per port
broker_host localhost
broker_port 1883
username / password password takes a secret reference
client_id
qos 0 0, 1 or 2
retain false on published messages
keepalive 60 seconds
timeout 10.0 seconds before a broker operation is given up on
publish_queue_size 256 payloads that may wait for the broker; past this the oldest is dropped and the node reports degraded
json_key key to lift out of an object payload; one for every port, or {"port": "key"}

A topic may be a filter: + matches one level, # the rest. Everything a filter matches lands on the same port, so use one port per topic where the difference matters.

json_key is for a device that wraps its reading: Victron publishes {"value": 47} on every path. Without it, a payload object is unwrapped only when it happens to carry the port's own name as a key.

HTTP

http — receive data on a webhook, or send it to a URL.

Outputs only makes it a webhook: the engine mounts a route at /hooks/{flow}/{url}/{secret} while the flow runs. Inputs make it a sender.

Setting Default Notes
url the route path in webhook mode, the full URL in sender mode
method POST GET or POST
timeout 30 seconds, sender mode
headers {}
query {} fixed query parameters; a value may be a secret reference
send_inputs true off when the inputs only trigger the request
secret shared secret appended to the webhook URL; takes a secret reference

!!! warning "A webhook with no secret is open to anyone who can reach the host."

The canvas flags this as an advisory issue rather than stopping the flow,
because a webhook on a private network is a legitimate thing to want.

InfluxDB

influxdb — write measurements to a bucket, or read them back.

Setting Notes
url, token, org, bucket connection; token takes a secret reference
write_precision ns, us, ms (default) or s
query_range default window for queries, e.g. -1h
writes per-input: measurement, field, tags
queries per-output: measurement, field, tags, range, aggregation
{
  "writes": {
    "living_temperature": {
      "measurement": "environment",
      "field": "temp_c",
      "tags": {"room": "living"}
    }
  }
}

Query passthrough. An incoming message holding a flux key is run as written, and the rows come back on the first output port as {"rows": [{ts, value, field, measurement, tags}], ...}.

A database node holds the credentials and the connection, nothing else. Building a query and shaping its rows are Function nodes on either side.

Notification

ntfy — push an incoming value to a phone through ntfy.

Setting Default Notes
server https://ntfy.sh
topic required
title
priority default min, low, default, high, urgent
tags comma-separated ntfy tags
token for a protected topic; takes a secret reference

This is a flow deciding something is worth saying. The engine reporting that it broke is Alerts, which is a different thing configured elsewhere.

Timing

Inject

inject — emit a value on request, on a timer, or when the flow starts.

Setting Default Notes
payload current time what to emit
payloads {} per-output-port payloads, keyed by port
interval 0 emit every n seconds; 0 means never on its own
cron a five-field cron expression
at_start false emit once when the flow starts
start_delay 1.0 how long to wait before that first emission

The scheduler: a cron expression here is what makes a flow run by the clock. It is also the most-placed node in a real instance, mostly as a button someone presses.

Delay & schedule

delay — hold messages back, limit their rate, or emit on a schedule.

Setting Default Notes
delay 0 seconds to hold each message, fractional
interval 0 minimum seconds between forwards
mapping {} input port → output port; paired in order when empty
cron five-field expression

Order of operations: rate check → delay → forward. With a cron and no inputs it emits the current time on each tick; with inputs it emits the last value it received.

The engine sleeps until a delay is due rather than polling for it, so a delay fires within a few milliseconds of its deadline on an idle engine, and a due timer is taken off the queue before work that is merely waiting. It is not a real-time guarantee: the wait ends on a normal thread, and a node that then needs a busy cascade slot waits for one.

!!! note "Not in a batch flow"

A rate limit holds a value back for a timer to release, and a run has no
timer, since the value would be dropped rather than delayed. Submitting a batch
flow with a rate-limited port is refused instead.

Trigger

trigger — send one value now and another once things go quiet. Despite the name, a debounce and hold rather than a scheduler: everything it sends starts from a value arriving. For a cron tick, see inject.

Setting Default Notes
first true sent as soon as a value arrives
then false sent when the wait expires; empty sends nothing
wait 60 seconds of quiet before the second value
extend true a value arriving during the wait starts it over
passthrough false send the incoming value instead of first
wait_port an input carrying the wait, when it differs per message

The shape this exists for: the door opened, so turn the light on, and off again in two minutes unless it opens again.

wait_port covers the case where how long to wait is itself a value: a rollershutter takes 26 seconds up and 28 down, so the node that decides the direction says how long to run for as well. A wait of zero or less sends nothing afterwards, and still cancels whatever the last message scheduled, which is how a stop is commanded exactly once.

Logic

Switch

switch — send a value down one branch or another, by rule.

Each rule names an output port; a matching value leaves through that port. Comparisons: eq, ne, gt, gte, lt, lte, contains, between.

Setting Default Notes
rules [] checked in order; each names the output it routes to
stop_at_first true leave through the first matching rule only
otherwise output for a value that matched nothing

Comparing a string to a number is a mistake in the rule, not a reason to take the flow down: the rule simply does not match.

Change

change — scale, offset, round or map a value on its way past.

Setting Default Notes
scale 1.0 multiply numbers by this
offset 0.0 then add this
round_to decimal places
mapping {} replace a value with another, looked up as text
default value when the lookup misses; empty passes it through

Filter unchanged

rbe — pass a value on only when it has actually changed.

Setting Default Notes
deadband 0.0 ignore numeric changes smaller than this
deadband_percent false read the deadband as a percentage

The cheapest fix for a sensor that reports every second and changes every hour.

Join

join — gather several inputs into one object or list.

Setting Default Notes
mode object object or array

System

Command

exec — run a command in the engine's container and read its output.

Setting Default Notes
command the command to run
append_payload false add the incoming value as one argument
timeout 30 seconds
fail_on_error false treat a non-zero exit as a node failure rather than output

Outputs the command's stdout, stderr and exit code.

!!! warning "Inside the container, not on the host"

A flow ported from something that read the host's journal, or poked a host
script, needs either a mount or a small listener on the host side. This
node cannot see the host.

File

file — read a file into the flow, or write one out of it.

Setting Default Notes
path relative to the engine's files directory
mode read read, write or append
format text text or json
newline true end each written record with a newline

Confined to a directory the engine owns. A flow that could name any path would be a way to read the secrets store or overwrite a node's source.

Numeric

Perceptron

mlp — a small neural layer over its numeric inputs.

output = weights @ inputs + biases, with weights drawn from seed so a node reloads identically.

Setting Default Notes
seed 0

A worked example of numeric logic, not a modelling tool. To train something, use a batch flow and a Function node.

Connectors

Anything else in the palette came from an installed connector package: a node type written against a published contract and discovered through the fluksio.node_types entry point group. The editor shows which package supplied it.

A connector declares its contract version, and one written for a version this engine does not speak is ignored rather than half-loaded. Installing or upgrading one takes effect on the next engine restart, because Python does not re-import a changed module and a rescan would promise more than it delivers.

The contract itself is in The connector contract, and Writing a connector walks through building one.

See also