Make the docs state things rather than argue them
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

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
This commit is contained in:
2026-08-31 10:49:58 +02:00
co-authored by Claude Opus 5
parent 2422a9b22b
commit bdad6d7fc2
25 changed files with 450 additions and 479 deletions
+25 -27
View File
@@ -20,7 +20,7 @@ in this interface a lot, so it is worth learning it properly.
└── workers on other boxes (optional)
```
One engine holds every flow. Nothing here is a plugin you install separately
One engine holds every flow. Nothing here is a plugin you install separately:
the broker client, the time-series writer, the dashboards and the alerting are
all part of the same process, editing the same graph.
@@ -28,7 +28,7 @@ all part of the same process, editing the same graph.
- Docker and Compose v2 on the host
- The address you will type into a browser. On a home network that is the
server's own address `192.168.1.50` and nothing else is needed. A
server's own address, `192.168.1.50`, and nothing else is needed. A
hostname is optional, and covered below
- Optionally: an MQTT broker and an InfluxDB you already run. If not, the stack
can start both for you
@@ -53,15 +53,15 @@ $EDITOR .env # FIRST_SUPERUSER, ENVIRONMENT=production
| `FIRST_SUPERUSER_PASSWORD` | leave it as `changethis` and one is generated for you |
| `ENVIRONMENT` | `production` closes the interactive API schema; `local` leaves it open |
Everything the instance owns the database, your flows, secrets,
artifacts, the packages your node code imports is on one Docker volume.
Everything the instance owns (the database, your flows, secrets, artifacts,
the packages your node code imports) is on one Docker volume.
Backing that volume up is backing up the instance.
??? note "Even smaller: no Docker at all"
`pip install fluksio && fluksio serve` gives you the same engine with no
containers, keeping its data in `~/.fluksio`. What it does not give you is
the web interface, which the SPA container serves so you would drive it
the web interface, which the SPA container serves, so you would drive it
from the API, or [pair it with a portal](../interface/portal.md) that
serves the dashboard for you. Good for a Raspberry Pi that only runs flows;
less good as your main instance.
@@ -82,7 +82,7 @@ APP_PORT=8080 docker compose -p fluksio-app --env-file .env \
-f docker/compose.yml -f docker/compose.lan.yml up -d --build
```
Open `http://192.168.1.50:8080`. That one address is the whole application
Open `http://192.168.1.50:8080`. That one address is the whole application:
the interface, the API under `/api`, and the websocket that puts live values on
the canvas.
@@ -96,13 +96,13 @@ maintaining: the browser is talking to one origin.
A published port is plain HTTP with no certificate. Between your own
machines that is fine, and it is not something to forward from a router. To
reach the house from outside, put a reverse proxy with TLS in front of it
reach the house from outside, put a reverse proxy with TLS in front of it,
or [pair it with a portal](../interface/portal.md), which needs no inbound
route at all.
### With a hostname, behind a reverse proxy
If you have DNS or want certificates set `DOMAIN` and run `make up`
If you have DNS, or want certificates, set `DOMAIN` and run `make up`
instead. The interface is then served on `app.${DOMAIN}` and the API on
`api.${DOMAIN}`, and nothing is published on a host port.
@@ -113,7 +113,7 @@ attach it to the `proxy` network and forward `app.${DOMAIN}` →
!!! tip "Developing on the same machine"
`make dev-lan` is the same idea for the integrated dev stack the app on
`make dev-lan` is the same idea for the integrated dev stack: the app on
`http://<host-ip>:8080`, `APP_PORT` to move it. `make dev` rebuilds without
the published port, so re-run `make dev-lan` after one.
@@ -128,9 +128,8 @@ Go to **Flows → New flow** and call it `house`. You land on the canvas.
A flow is a set of nodes that talk to each other through **named messages**.
You do not draw wires: a node says which messages it needs and which it
produces, and the canvas draws the graph that follows from those names. That
sounds like a small difference and turns out to be a large one — renaming is
safe, fan-in is free, and two flows can share a value by naming it.
produces, and the canvas draws the graph that follows. Renaming is safe, fan-in
is free, and two flows share a value by naming it.
### Read a sensor
@@ -150,8 +149,8 @@ broker sends one. Click the wire to see the last payload and its history.
### Do something with it
Add a **Function** node. This is a Python node the code editor opens in its
panel:
Add a **Function** node. This is a Python node, and the code editor opens in
its panel:
```python
def process(living_temperature, comfortable=21.0):
@@ -160,7 +159,7 @@ def process(living_temperature, comfortable=21.0):
```
Declare `living_temperature` as an input (type `float`) and `heat_wanted` as an
output (type `bool`). `comfortable` is not a port — it is a **setting**,
output (type `bool`). `comfortable` is not a port but a **setting**,
because it is a constant of this node rather than something the graph carries.
It shows up as a field in the node's Settings section.
@@ -178,8 +177,8 @@ Publish the flow. You have a thermostat.
!!! warning "Test before it touches a relay"
A flow can be **paused** (it holds messages instead of running them) and
**stepped** (release exactly one). Together with the run button which
injects a value by hand that is how you convince yourself the logic is
**stepped** (release exactly one). Together with the run button, which
injects a value by hand, that is how you convince yourself the logic is
right before the contactor finds out. Both live on the dock at the bottom
of the canvas.
@@ -212,11 +211,10 @@ point.
Note the token. Credentials never sit in a flow: `{"$secret": "influx-token"}`
is a reference into an encrypted store, and the editor renders those fields as
a secret picker. Add the actual value once under **Secrets**. Flows are a git
repository you might well push somewhere this is what keeps a password out of
it.
repository you might well push somewhere, so this keeps a password out of it.
Reading back is the same node with `queries` instead of `writes`, or for
anything a chart asks for a pair of small Python nodes on either side that
Reading back is the same node with `queries` instead of `writes`, or, for
anything a chart asks for, a pair of small Python nodes on either side that
build a Flux query and shape its rows. That indirection is deliberate: the
database node holds the connection and nothing else, so a dashboard widget
never learns which database answered it.
@@ -231,7 +229,7 @@ Widgets are typed the same way ports are: a switch binds to a `bool`, a gauge
to a number, an agenda to a `list`. Bind it wrong and the editor says so rather
than drawing nothing.
Controls work in the other direction a switch on a dashboard publishes the
Controls work in the other direction: a switch on a dashboard publishes the
message it is bound to, exactly as a node would. The canvas draws it as a
labelled endpoint feeding the nodes that read it, so a value never appears from
nowhere.
@@ -247,7 +245,7 @@ lives on a different network, or something needs hardware the engine's host
does not have.
The unit of distribution is the **worker**. It runs the code of nodes you mark
for it, and it dials *out* to the engine so the Pi in the shed does not need
for it, and it dials *out* to the engine, so the Pi in the shed does not need
an inbound route, and the engine does not need to reach it.
On the engine, mint a token:
@@ -276,7 +274,7 @@ flow before the hardware arrives.
!!! note "What a worker is not"
It is not a second engine. Subscriptions, schedules, webhooks and the
dashboards all stay in one process — that is what keeps a value having one
dashboards all stay in one process, which keeps a value having one
definition. A worker executes node bodies, nothing else. Scaling *the
engine* to several processes is not supported: run one.
@@ -291,9 +289,9 @@ What you probably want on day one is everything, to ntfy:
- add an ntfy channel with your server and topic
- add a rule with no events ticked, which means *all of them*
The engine deduplicates aggressively the same node failing every second is
one alert, not thirty-six thousand and caps the total at ten an hour however
bad it gets. See [Secrets, modules and alerts](../interface/operations.md).
The engine deduplicates aggressively, so the same node failing every second is
one alert rather than thirty-six thousand, and caps the total at ten an hour
however bad it gets. See [Secrets, modules and alerts](../interface/operations.md).
## Keep it alive