Four things the python SDK turned up, each fixed where every client sees it. A key no port declares is now an error rather than a silent drop, on the return, the yield and the emit alike — the contract the docs already stated. The SDK reads literal yields at sync time, so a typo fails before anything runs, and an emission of one fails the call rather than being logged where nobody looks. NaN and infinity are refused at the port. JSON cannot spell either, so one that travelled came back as a 500, a socket frame that stopped the canvas, or a metric batch the database dropped whole. An artifact input takes `@run:<id>.<output>` or a bare digest, resolved on the engine — so the CLI, the run dialog and a python caller mean the same thing, and a sweep can pass one at all. Node timeouts are off by default. The clock measured silence, which a training node is full of, and remote workers had already stopped enforcing it — their heartbeat reset it. Now a heartbeat proves the agent rather than the node, ninety seconds of nothing fails the call either way, and the engine touches work it is still running so a long node is not redelivered at sixty seconds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019V5bsYGNxcgPs4xXmTPx69
172 lines
7.2 KiB
Markdown
172 lines
7.2 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; `~/.fluksio` with `--global`) | 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 |
|
|
| `NODE_VENV` | `auto` | which interpreter node code runs on — see below |
|
|
|
|
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`.
|
|
|
|
`NODE_VENV` is the exception, being about an environment rather than a path:
|
|
|
|
| Value | What node code runs on |
|
|
|---|---|
|
|
| `auto` (default) | the venv Fluksio was installed into, when it was installed into one and there is no venv of its own already built. `pip install fluksio` beside your own packages is this case, and the packages are then already there — the Modules screen turns read-only, because that environment is not Fluksio's to install into. |
|
|
| `managed` | a venv the engine builds under `DATA_DIR` and owns, which the Modules screen installs into with `uv pip sync`. The container images set this: the venv in them holds the app and nothing of anybody else's. |
|
|
| a path | that interpreter, or that venv, whatever it is. |
|
|
|
|
An installation that already has a managed venv keeps it on upgrade under
|
|
`auto`, because it may hold packages somebody installed on purpose.
|
|
|
|
!!! 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 |
|
|
|
|
The compose stack takes it from `.env`, falling back to `https://api.${DOMAIN}`.
|
|
`scripts/setup.sh` writes it there with the scheme `ENVIRONMENT` implies, so a
|
|
local build calls `http://` and does not fail a certificate check nothing is
|
|
there to satisfy.
|
|
|
|
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` | `0` | seconds a node may be silent, unless it sets its own; 0 is no limit |
|
|
| `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)
|