Files
app/docs/reference/configuration.md
T
stroblmeandClaude Opus 5 d3db58310b Getting started: reach the app by address, without DNS
The guide asked for a hostname and served the interface on
`app.${DOMAIN}` with the API on `api.${DOMAIN}` — which is not what
someone with a box in a cupboard sets up, and could not be made to work
by typing an address into a browser. `compose.lan.yml` already solves it
for the dev stack; the same file layers onto the production one, so the
guide now opens with that and keeps the hostname as the other option.

The reference gained the setting behind it. `VITE_API_URL` is a build
argument of the frontend image rather than something the stack reads,
which is worth saying once: empty means the interface addresses the API
relative to whatever origin served it, so one image answers on an
address, a hostname and an ssh tunnel alike, and no CORS list has to be
kept in step.

Verified against the production compose layered with `compose.lan.yml`:
the page, `/api` on the same port, a cross-origin-free login, the flow
websocket upgrading through nginx, and `/docs` still refused there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 09:49:50 +02:00

148 lines
5.6 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 |
`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)