Server Settings Console¶
The Server settings console is a browser editor for the whole of
oduflow.toml: every global section and every team. It is served at /admin
in HTTP mode, so after the first start you never have to SSH into the server
to change the configuration by hand.
It is a deployment-wide tool for whoever operates the server, not part of the team dashboard. It has its own password, separate from every team login, because it can read and change every team's tokens and passwords.
Enable it¶
Fresh installs generate a console password on first start. Read it from the config file:
On an existing install, enable the console once from the server shell, then restart Oduflow:
Open /admin on any hostname the server answers on, for example
https://dev.example.com/admin (Traefik) or http://server:8000/admin
(port mode). While [admin] password is empty the console does not exist:
every /admin path answers 404.
For authenticator-app 2FA, run oduflow ui-2fa setup --admin on the server
(see CLI Reference).
What you can edit¶
| Section | What it covers |
|---|---|
| Server | Listener, local-path mounts, insecure-HTTP switch, tracing, telemetry |
| Routing | Port or Traefik mode, TLS, ACME e-mail, public URL scheme |
| Extra routes | [route.*] Traefik routes to services Oduflow does not manage |
| Teams | Add and remove teams; per team: hostname, base domain, credentials, port range, slots, quotas, coding agent and its environment, image registry |
| Lifecycle | Auto-stop, auto-delete, deleted-production purge |
| Coding agent | Agent image and model overrides |
| Production | Production hosting, workers cap, WAL safety thresholds |
| Backups | S3 backups: enable, credentials, schedule, retention |
| Cluster DR | Cluster WAL archiving status and control, base-backup inventory, whole-cluster PITR |
| Storage, Database | Shown read-only (see below) |
| Console access | The console's own password |
| Raw TOML | The whole file, for anything the forms do not cover |
| History | Every save, with a diff and one-click revert |
Every field shows its TOML key, the default that applies when the key is absent, and a badge saying when a change takes effect. Clearing a field removes the key from the file, so the default applies again. The file keeps your comments and layout: a key that the bundled template documents as a commented-out line is uncommented in place.
When a change takes effect¶
| Badge | Meaning |
|---|---|
| Live | Applied the moment you save — slots, quotas, lifecycle hours, team dashboard passwords, WAL thresholds, backup schedule and more |
| Restart | Saved now, used after Oduflow restarts — auth tokens, new teams, hostnames, ports, agent settings, production and backup enablement, extra routes |
| Recreate | Needs a restart, and existing environments, services and productions keep the old value until each is recreated — routing mode and TLS |
| Read-only | Changing the file alone would break the deployment — the PostgreSQL user, password and image, and the data directory |
The console applies live changes on its own and keeps everything else out of the running process until a restart, so the server never runs half on the old configuration and half on the new one. When saved changes are waiting, a banner lists them with a Restart server button. The restart stops the server gracefully and starts it again with the same command line; environments, services and productions keep running. The console refuses to restart while an operation (a deploy, an environment build) holds a lock.
Read-only keys need work outside Oduflow first, for example ALTER ROLE for
the database password or moving the data directory. Do that, then change the
key in Raw TOML.
Saving safely¶
- Edits collect in a draft. Review & save shows each change, when it takes effect, and the file diff with secrets masked.
- The candidate file is validated with the same parser and checks the server runs at startup, including the HTTP authentication rules. A configuration the next start would refuse cannot be saved.
- The file is replaced atomically, with its owner and owner-only mode kept. If it changed since you opened it, by a hand edit or another admin, the save is refused and you reload instead of overwriting.
- The previous and new versions go to
.oduflow-history/next to the config (owner-only, last 50 saves). History shows each save's diff, and Revert restores the file as it was before that save, through the same review.
If the file on disk is invalid (for example after a broken hand edit), the console says so and opens Raw TOML so you can fix it. The running server keeps its last good configuration in the meantime.
Teams¶
Add team suggests the next team ID and the next free 100-port block, and generates a dashboard password and an MCP token. Copy them before saving. The new team becomes reachable after a restart.
Remove team removes the [team.<id>] section only. The team's data
directory, databases and running containers are not deleted; clean them up
separately.
Secrets¶
Secrets (passwords, tokens, S3 keys, registry tokens, agent environment
values) never reach the browser until you press Reveal on one field, and
each reveal is logged with the client address. Leave a secret field empty to
keep the stored value, or press Generate for a new random one. Diffs and
history show ******** instead of values, plus (changed) where a secret
changed.
Cluster disaster recovery¶
The Cluster DR section operates on the shared production PostgreSQL cluster — every team's productions at once — which is why it lives here and not on any team dashboard or MCP surface:
- WAL archiving: the cached monitor state (queue, disk reserve,
disk-protection latch, storage preflight) with the cluster-wide actions
pause/resume/retry, andrecover/releasewhile disk protection is latched. Every action asks you to typeALL-PRODUCTIONS. - Cluster restore (PITR): the base-backup inventory from S3 and a restore
form (optional target time). The restore stops every production, rewrites
the cluster from WAL-G, and restarts the productions it stopped; it asks you
to type
RESTORE-CLUSTER. The previous data directory is displaced inside the Docker volume, not destroyed.
The same actions exist on the owner CLI (oduflow wal,
oduflow restore-cluster) for headless servers and for resurrecting a cluster
on a fresh install — see CLI and
Production Hosting.
Security model¶
- Separate credential:
[admin] passwordmust differ from every teamui_password,auth_tokenandproduction_token. The console accepts a new password only if it has at least 12 characters. - Separate session: a signed cookie with its own salt, valid for 12 hours,
HttpOnly,SameSite=Strict. Changing the console password or its 2FA signs every console session out. - Team sessions never reach
/admin, and a console session does not open the team dashboard. - State-changing requests from another origin are rejected, and failed sign-ins are rate-limited per client address.
- Saves, reveals, sign-ins and restarts are written to the server log without values.
- The console does not exist for MCP clients. Agents cannot change the server configuration.
To turn the console off, run oduflow admin disable on the server and
restart.
REST API¶
The console page uses a small JSON API under /admin/api/. It needs the
console session cookie; every POST must be same-origin.
| Method | Path | Purpose |
|---|---|---|
| GET | /admin/api/state |
Schema, current values with secrets masked, revision, pending changes |
| POST | /admin/api/preview |
Validate a candidate and return changes, apply classes and the masked diff |
| POST | /admin/api/apply |
Save a candidate if revision still matches; apply live values |
| POST | /admin/api/reload |
Apply live values from the file on disk (after a hand edit) |
| POST | /admin/api/reveal |
Return one secret value |
| GET | /admin/api/raw |
The whole file and its revision |
| GET | /admin/api/history |
Saved versions, newest first |
| GET | /admin/api/history/{id} |
One save's masked diff |
| GET | /admin/api/wal |
Cluster WAL monitor state plus the WAL-G base-backup inventory |
| POST | /admin/api/wal/control |
Body {"action": "pause\|resume\|retry\|recover\|release", "confirm": "ALL-PRODUCTIONS"} — cluster-wide WAL action under the system lock |
| POST | /admin/api/pitr |
Body {"target_time": "...", "confirm": "RESTORE-CLUSTER"} — whole-cluster PITR under the system lock |
| POST | /admin/api/restart |
Restart Oduflow (refused while operations hold locks) |
preview and apply take one candidate: {"changes": [{"path": [...],
"value": ...} | {"path": [...], "unset": true}], "revision": "..."}, or
{"raw": "<whole file>", "revision": "..."}, or {"restore": "<history id>",
"revision": "..."}.