Four small things, each with a device behind it.
An MQTT filter now routes what it subscribed to. `+` and `#` reached the
broker and were then looked up in an exact-match dict, so every message a
wildcard subscription received was dropped in silence.
`json_key` lifts a value out of the object a device wraps it in — Victron
publishes `{"value": 47}` on every path, which was otherwise a Python node
per port.
The trigger node learned `passthrough` and `wait_port`, because how long to
wait can be a value rather than a constant: a rollershutter takes 26 seconds
up and 28 down. A wait of zero sends nothing afterwards and still cancels
what the last message scheduled, which is how a stop is commanded once
instead of forever.
The HTTP sender takes fixed `query` parameters, so an API key is a secret
reference rather than a message on the canvas, and `send_inputs` off for a
request whose inputs are only a trigger.
Also: `delay` accepts fractional seconds, and `TZ` reaches the container, so
a cron expression means local time. Left unset it is UTC, as before.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
155 lines
6.0 KiB
Markdown
155 lines
6.0 KiB
Markdown
# Configuration
|
|
|
|
Every setting comes from the environment, or from an env file. Which file
|
|
depends on how the installation was started:
|
|
|
|
| Started with | Reads |
|
|
|---|---|
|
|
| `fluksio serve` | `env` inside the data directory (`$FLUKSIO_ENV_FILE`) |
|
|
| the Docker stack | `.env` beside `docker/` |
|
|
|
|
Anything already exported wins over the file.
|
|
|
|
## Storage
|
|
|
|
| Variable | Default | Notes |
|
|
|---|---|---|
|
|
| `DATA_DIR` | `flow-data` (`~/.fluksio` via the CLI) | everything below derives from this |
|
|
| `DATABASE_URL` | SQLite in `DATA_DIR` | any SQLAlchemy URL |
|
|
| `FLOWS_DIR` | `$DATA_DIR/flows` | the git repository holding flows |
|
|
| `SECRETS_FILE` | `$DATA_DIR/secrets.enc` | encrypted credentials, deliberately outside the repo |
|
|
| `ALERTS_FILE` | `$DATA_DIR/alerts.json` | channels and rules |
|
|
| `PANELS_FILE` | `$DATA_DIR/panels.json` | wall-panel pairings |
|
|
| `OAUTH_PRIVATE_KEY_FILE` | `$DATA_DIR/oauth-key.pem` | signs agent and worker tokens |
|
|
| `CLOUD_CONFIG_FILE` | `$DATA_DIR/cloud.json` | the portal enrolment, if any |
|
|
|
|
Set `DATA_DIR` and the rest follow. Set one explicitly and it wins — which is
|
|
what the container images do to pin everything onto `/data`.
|
|
|
|
!!! warning "The four files that must be on persistent storage"
|
|
|
|
`secrets.enc`, `alerts.json`, `panels.json` and `oauth-key.pem` are written
|
|
at runtime. In a container, anything not on a volume lands in the writable
|
|
layer and is lost on the next rebuild — un-pairing every screen and
|
|
revoking every agent.
|
|
|
|
## State
|
|
|
|
| Variable | Default | Notes |
|
|
|---|---|---|
|
|
| `REDIS_HOST` | unset | without it, flow state lives in memory and does not survive a restart |
|
|
| `REDIS_PORT` | `6379` | |
|
|
|
|
Flow state is the last value of every message, node memory, and the run queue.
|
|
Redis here is persistence, not a cache — run it with append-only persistence
|
|
on.
|
|
|
|
## Identity and access
|
|
|
|
| Variable | Default | Notes |
|
|
|---|---|---|
|
|
| `SECRET_KEY` | generated | signs sessions and derives the secrets-store key |
|
|
| `ACCESS_TOKEN_EXPIRE_MINUTES` | `11520` (8 days) | |
|
|
| `FIRST_SUPERUSER` | — | absent means the CLI creates one on first run |
|
|
| `FIRST_SUPERUSER_PASSWORD` | — | absent means one is generated and printed once |
|
|
| `DOMAIN` | `localhost` | what the API and OAuth issuer are built from |
|
|
| `FRONTEND_HOST` | `http://localhost:5173` | used in mails, OAuth metadata and panel pairing links |
|
|
| `BACKEND_CORS_ORIGINS` | `[]` | comma-separated; `FRONTEND_HOST` is always allowed |
|
|
|
|
## Where the interface looks for the API
|
|
|
|
The dashboard is a static bundle, so this one is a **build** argument of the
|
|
`frontend` image rather than a setting the running stack reads.
|
|
|
|
| Argument | Used by | Effect |
|
|
|---|---|---|
|
|
| `VITE_API_URL` | `frontend` at build time | the address the interface calls |
|
|
|
|
Empty is the useful value: the interface then addresses the API relative to
|
|
whichever origin served the page, so one image answers on a hostname, on a
|
|
`http://<host-ip>:<port>`, and through an ssh tunnel alike — and no origin has
|
|
to be added to `BACKEND_CORS_ORIGINS`, because there is only one.
|
|
`docker/compose.lan.yml` builds it that way and puts an `/api` proxy in front
|
|
of the backend to complete it; see
|
|
[getting started](../getting-started/facility-automation.md#on-your-own-network-by-address).
|
|
|
|
Set it to an absolute URL only when the API genuinely lives somewhere else, and
|
|
remember it is fixed at build time: changing it means rebuilding that image.
|
|
|
|
!!! danger "Rotating `SECRET_KEY`"
|
|
|
|
The secrets store is encrypted with a key derived from it. Change it and
|
|
the store stops decrypting, and every session is signed out. Re-enter your
|
|
secrets, or plan the rotation properly.
|
|
|
|
## Environment
|
|
|
|
| Variable | Default | Notes |
|
|
|---|---|---|
|
|
| `ENVIRONMENT` | `local` | `local`, `staging` or `production` |
|
|
| `PRIVATE_API_ENABLED` | `false` | unauthenticated test-only endpoints; needs `ENVIRONMENT=local` too |
|
|
| `TZ` | `UTC` | the timezone every schedule is written in |
|
|
|
|
`TZ` is the container's own, not a setting the code reads: an `inject` or a
|
|
`delay` with a cron expression fires on local time. Left at `UTC`, "off at
|
|
02:00" means two in the morning UTC, which in most of the world is neither two
|
|
o'clock nor the same hour in summer as in winter. Set it to where the
|
|
installation is.
|
|
|
|
`production` closes `/docs`, `/redoc` and the OpenAPI document, because the
|
|
schema enumerates every endpoint the installation serves — including the paths
|
|
webhook nodes mounted at runtime. It also turns a `changethis` secret from a
|
|
warning into a refusal to start.
|
|
|
|
## The engine
|
|
|
|
| Variable | Default | Notes |
|
|
|---|---|---|
|
|
| `FLOW_MAX_WORKERS` | `4` | node-code subprocesses run in parallel |
|
|
| `FLOW_NODE_TIMEOUT` | `30.0` | seconds a node may run, unless it sets its own |
|
|
| `OBS_RETENTION_DAYS` | `30` | how long metrics, events and run records are kept |
|
|
|
|
## Agents
|
|
|
|
| Variable | Default | Notes |
|
|
|---|---|---|
|
|
| `MCP_ENABLED` | `false` | opens the `/mcp` endpoint **and** OAuth client registration |
|
|
| `MCP_TOKEN_EXPIRE_MINUTES` | `60` | an agent's token is a bearer secret held by a program |
|
|
| `MCP_REFRESH_EXPIRE_DAYS` | `30` | |
|
|
| `OAUTH_CODE_EXPIRE_SECONDS` | `60` | |
|
|
|
|
See [Agents over MCP](../code/agents.md).
|
|
|
|
## Mail
|
|
|
|
Needed for password-reset mails. Without `SMTP_HOST` and `EMAILS_FROM_EMAIL`,
|
|
mail is simply off.
|
|
|
|
| Variable | Default |
|
|
|---|---|
|
|
| `SMTP_HOST` | — |
|
|
| `SMTP_PORT` | `587` |
|
|
| `SMTP_USER` / `SMTP_PASSWORD` | — |
|
|
| `SMTP_TLS` / `SMTP_SSL` | `true` / `false` |
|
|
| `EMAILS_FROM_EMAIL` | — |
|
|
| `EMAILS_FROM_NAME` | `Fluksio` |
|
|
| `EMAIL_RESET_TOKEN_EXPIRE_HOURS` | `48` |
|
|
|
|
## Monitoring
|
|
|
|
| Variable | Default | Notes |
|
|
|---|---|---|
|
|
| `SENTRY_DSN` | — | error reporting, if you want it |
|
|
|
|
## Health check
|
|
|
|
`GET /api/v1/utils/health/` is a *deep* check: it fails when the event loop is
|
|
wedged or the state backend is gone, not just when the process is up. That is
|
|
what the container healthcheck probes, and what an autoheal sidecar restarts
|
|
on.
|
|
|
|
## See also
|
|
|
|
- [The `fluksio` command](../code/cli.md) — what the data directory holds
|
|
- [Getting started: facility automation](../getting-started/facility-automation.md)
|