Skip to Content
ReferenceConfiguration & Env Vars

Configuration & Env Vars

RondoFlow is configured through environment variables. There is a single .env file at the repo root — every package (UI, server, docs) reads from it, so you never juggle per-package config. npm run setup generates this file from .env.example and fills in a random BETTER_AUTH_SECRET for you.

Most settings are environment variables, but two things live elsewhere: the runtime credentials in Settings → Credentials (which override .env at boot — see Runtime credentials override .env), and the global per-run budget cap, which is stored as a database Policy row rather than an env var (see Things that are not env vars). Anything not listed on this page has no effect on the running app.

How .env is loaded

The server is launched with Node’s native --env-file=../../.env flag (the dev, start, and seed scripts all set it) — RondoFlow does not use the dotenv package. The practical consequence: if you run the compiled server (dist/index.js) by hand without that flag, no .env is loaded and the process falls back to whatever is already in process.env. Always start the server through the package scripts, or pass --env-file yourself.

Quick start

npm run setup # copies .env.example → .env and generates BETTER_AUTH_SECRET

Then open .env and set one Claude credential plus any optional provider keys, SMTP settings, or OAuth apps you want.

Variable reference

Core

VariableRequiredDefaultPurpose
DATABASE_URLYespostgresql://rondoflow:rondoflow_dev@localhost:5432/rondoflowPostgreSQL connection string used by Prisma.
BETTER_AUTH_SECRETYesnone (generated by npm run setup)Signing secret for Better Auth sessions. No hardcoded fallback — must be set. Also the fallback key for credential encryption (see below).
BETTER_AUTH_URLYeshttp://localhost:3001Public origin of the auth/API server.
PORTNo3001Port the Fastify API server listens on.
UI_ORIGINNohttp://localhost:3000Origin of the Next.js UI; used for CORS and redirects.

Claude authentication

Provide one of the two credentials below. The server validates that at least one is present before Assistants (Agents) can run.

VariableRequiredDefaultPurpose
ANTHROPIC_API_KEYOne of theseemptyStandard Anthropic API key (billed per token). In .env.example.
CLAUDE_CODE_OAUTH_TOKENOne of theseemptySetup token from claude setup-token, which uses your Claude subscription. In .env.example.

Precedence: if both are set, the setup token (CLAUDE_CODE_OAUTH_TOKEN) wins and is the only credential forwarded to the spawned CLI. This prevents an ambient ANTHROPIC_API_KEY from silently overriding the token you intended to use. Both can also be configured in Settings → Credentials rather than the .env file.

Other providers

Unlike the Claude credentials above, these two keys are not in .env.example. They are managed primarily as runtime credentials in Settings → Credentials (groups openai and perplexity). An .env fallback works only if you add the variable by hand.

VariableRequiredIn .env.example?Purpose
OPENAI_API_KEYNoNo — Settings → CredentialsAPI key for OpenAI-backed Assistants.
PERPLEXITY_API_KEYNoNo — Settings → CredentialsAPI key for Perplexity-backed Assistants.

See Providers for how each provider maps to an Assistant.

OAuth sign-in (optional)

Leave these empty to disable the corresponding social login. All four are in .env.example and can also be set in Settings → Credentials.

VariableRequiredDefaultPurpose
GITHUB_CLIENT_IDNoemptyGitHub OAuth app client ID.
GITHUB_CLIENT_SECRETNoemptyGitHub OAuth app client secret.
GOOGLE_CLIENT_IDNoemptyGoogle OAuth client ID.
GOOGLE_CLIENT_SECRETNoemptyGoogle OAuth client secret.

Email (SMTP)

These power the canvas Email node (Card), which sends a workflow’s combined Assistant output as an HTML email. The Email node is opt-in — its enabled toggle defaults off, so building or running a workflow never sends mail until you explicitly enable the node. Leave SMTP_HOST blank to disable email entirely. See the Data Nodes guide for the node’s UX and the Email node behavior below.

VariableRequiredDefaultPurpose
SMTP_HOSTTo enable emailemptySMTP server hostname. Blank disables the Email node.
SMTP_PORTNo587SMTP port.
SMTP_SECURENofalsetrue for port 465 (implicit TLS); false for 587 (STARTTLS).
SMTP_USERNoemptySMTP username (omit for unauthenticated relays).
SMTP_PASSNoemptySMTP password. Secret — encrypted when stored as a runtime credential.
SMTP_FROMTo enable emailemptySender address, e.g. RondoFlow <noreply@example.com>.

A send requires both SMTP_HOST and SMTP_FROM — if either is blank, readSmtpConfig() returns no config and the Email node does nothing. All SMTP_* variables are in .env.example and settable in Settings → Credentials (group smtp, with SMTP_PASS encrypted); a DB-stored value overrides the .env value at boot.

Email node behavior

  • Recipients are connection-driven (read from the edges into the node) and de-duplicated per chain run, so each address receives one message even if multiple Assistants feed the node.
  • The HTML body is rendered from the run’s combined output via formatRunOutput.
  • The headless scheduler honors Email nodes, so a scheduled workflow can email its result without anyone watching.
  • The backend exposes POST /api/email/send (run-capability gated, rate-limited to 10/min, with recipient/subject/HTML validation and CR/LF header-injection defense — failures return 502 EMAIL_SEND_FAILED) and GET /api/email/status. See the API reference.

First-admin bootstrap

RondoFlow is invite-only — open self-registration is disabled, so the very first account has to be created out of band. These variables seed an initial admin who can then invite everyone else from the Users panel.

VariableRequiredDefaultPurpose
RONDOFLOW_ADMIN_EMAILNoemptyEmail of the first admin to create at seed time.
RONDOFLOW_ADMIN_PASSWORDNoemptyPassword for the first admin.
RONDOFLOW_ADMIN_NAMENoAdministratorDisplay name for the first admin.

The bootstrap runs only at seed time (npm run db:seed, which npm run setup also invokes) — it is not a server-startup hook. It is skipped if either the email or password is blank, and is idempotent: if the user already exists it just ensures their role is admin. Because the Docker Compose migrate container only runs prisma migrate deploy (no seed), Docker users must run the seed step themselves — e.g. npm run db:seed with these vars set and PostgreSQL reachable — or they will be locked out, since self-registration is disabled.

See Security for the roles and invite-only model.

Execution & runtime

These tune how the process manager and the Claude Code spawner run Assistants. The defaults are sensible; reach for them when diagnosing stalls, capping cost, or running headless.

VariableRequiredDefaultPurpose
MAX_CONCURRENT_AGENTSNo5Maximum number of Assistants the process manager will run at once.
CLAUDE_CODE_MAX_OUTPUT_TOKENSNo128000Max output tokens per Assistant response. Claude Code clamps this to each model’s true max (Opus ~128k, Sonnet/Haiku ~64k), so the default is safe. In .env.example.
RONDOFLOW_SPAWN_IDLE_TIMEOUT_MSNo300000Idle timeout (ms) for a spawned Assistant — if it streams no event for this long it is torn down. Resets on every stream event, so it only fires on a true stall. Applies to every spawn; 0 disables it. Five minutes by default.
RONDOFLOW_SPAWN_MAX_MSNo0Absolute wall-clock cap (ms) on a single spawn regardless of activity. 0 disables the cap; one-shot generators set their own.
RONDOFLOW_TEARDOWN_ON_DISCONNECTNo1When 1, a user’s in-flight runs are torn down after their last socket disconnects (subject to the grace period). Set to 0 to keep runs alive across disconnects — only server shutdown then tears them down.
RONDOFLOW_TEARDOWN_GRACE_MSNo60000Grace window (ms) after a disconnect before teardown fires. A refresh, new tab, or reconnect within this window cancels it. One minute by default.
RONDOFLOW_DEBUG_SPAWNNounsetSet to 1 (or true) for verbose spawn-time tracing of the Claude Code CLI. Useful for diagnosing empty or failed Assistant output. In .env.example (set to 1).
IS_SANDBOXNounsetSet to 1 to tell the Claude Code CLI it is running in a sandbox. Required when running as root with bypassPermissions; the server auto-sets it for exactly that case. In .env.example (set to 1).

The teardown knobs are enforced by the unified run registry, which reaps in-flight runs (Agent, loop, chain, discussion, and PRD-pipeline) when a user’s last socket drops and on server shutdown. The spawn timeouts also matter for the scheduler: a stalled headless run (for example a plan-mode prompt that never streams) is reaped by RONDOFLOW_SPAWN_IDLE_TIMEOUT_MS. See Self-Hosting and Schedules.

RONDOFLOW_DEBUG_SPAWN=1 and IS_SANDBOX=1 ship enabled in .env.example to ease local debugging. Review whether you want them on for a production deployment.

Credential encryption key

Runtime credentials stored in the database (Settings → Credentials) are encrypted at rest. The encryption key is derived from an env var with a fallback.

VariableRequiredDefaultPurpose
rondoflow_SECRETNofalls back to BETTER_AUTH_SECRETKey used to encrypt/decrypt stored credentials (resources/encryption.ts).

Casing matters. encryption.ts reads process.env['rondoflow_SECRET'] — the variable name is lowercase rondoflow_SECRET, not the RONDOFLOW_SECRET (uppercase) you might expect from the rest of the RONDOFLOW_* family. On case-sensitive systems, setting RONDOFLOW_SECRET has no effect and the key silently falls back to BETTER_AUTH_SECRET. If you set a dedicated encryption key, use the exact casing rondoflow_SECRET, and don’t rotate it without re-encrypting existing credentials.

External folders

These control the host directory bind-mounted into the server so Assistants can read, write, and run git over real project folders. See External Folders.

VariableRequiredDefaultRead byPurpose
EXTERNAL_FOLDERS_HOST_PATHNo./externalDocker Compose onlyHost path bind-mounted into the server container (${EXTERNAL_FOLDERS_HOST_PATH:-./external}:/external:rw). Has no effect outside Docker.
EXTERNAL_FOLDERS_CONTAINER_ROOTNo/externalServer runtimeIn-container root all registered folders must resolve under (lib/external-folders.ts). Change only if you also change the mount target in docker-compose.yml.

UI build-time variables

The Next.js UI reads these public variables to find the API and Socket.IO origin. They are baked at build time (the NEXT_PUBLIC_ prefix), not read by the server, and are not in .env.example — set them in docker-compose.yml (the ui service does) or your build environment when the API lives somewhere other than localhost:3001.

VariableRequiredDefaultPurpose
NEXT_PUBLIC_API_URLNohttp://localhost:3001Base URL the UI calls for REST (lib/api.ts).
NEXT_PUBLIC_SOCKET_URLNohttp://localhost:3001URL the UI opens its Socket.IO connection to (lib/socket.ts).

Documentation proxy

VariableRequiredDefaultPurpose
DOCS_ORIGINNohttp://localhost:3002Upstream the UI reverse-proxies /docs to. The docs site runs with base path /docs, so users reach it at <host>/docs on the UI origin. In Docker Compose this is set to http://docs:3002.

For the standalone production build, the /docs rewrite origin is baked from the DOCS_ORIGIN build arg on the ui image (next build freezes absolute-URL rewrites into the routes manifest). The runtime DOCS_ORIGIN env is kept for dev-mode parity but does not change the production rewrite — set the build arg in docker-compose.yml.

The UI’s next.config.mjs rewrites both /docs and /docs/:path* to DOCS_ORIGIN, keeping the /docs prefix so HTML, _next/* assets, and the Pagefind search index all proxy through.

Things that are not env vars

A couple of operator-facing settings deliberately do not live in .env:

  • Global per-run budget cap. The UI’s “Max Budget” → --max-budget-usd is stored in the canonical global Policy row (id d0000000-0000-0000-0000-000000000001), managed via GET/PUT /api/settings/budget and capped at 1000 USD (null = no cap). There is no budget env var — don’t go looking for one. See Security.
  • Most credentials at runtime. Beyond .env, Claude/OpenAI/Perplexity keys, OAuth apps, and SMTP can all be set in Settings → Credentials and stored (encrypted where secret) in the database.

Runtime credentials override .env

At boot, loadSettingsIntoEnv() reads stored credentials from the database and writes them into process.env, overriding the matching .env values — so a DB-stored credential always wins over .env. Clearing a runtime credential removes it from the database; on the next boot the .env baseline (if any) is what’s loaded. This applies to every credential in the CREDENTIALS set: Claude auth, OpenAI/Perplexity, GitHub/Google OAuth, and the full SMTP_* group.

Ports

ServicePortNotes
UI (Next.js)3000Main app. Also serves the docs at <host>/docs via the proxy.
API (Fastify + Socket.IO)3001Set via PORT. Health check at /api/health.
Docs (Nextra)3002Served under base path /docs; reached through the UI proxy in normal use.
PostgreSQL5432Database.

In local development you can run the docs site directly on http://localhost:3002/docs. In production the docs Compose service is fronted by the UI proxy, so you only expose the UI port.

Docker Compose vs. local

In a local npm run dev, services read the root .env and default to localhost origins. Under Docker Compose, the same root .env supplies values via interpolation (for example ${ANTHROPIC_API_KEY:-}), but only a specific subset of variables is forwarded into the server container — the rest must be added to the Compose file (or set via Settings → Credentials where applicable).

The server service env block in docker-compose.yml forwards exactly:

PORT, DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL, UI_ORIGIN, ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and EXTERNAL_FOLDERS_CONTAINER_ROOT. (EXTERNAL_FOLDERS_HOST_PATH is consumed by the bind-mount line, not the env block; inter-service hosts also change — e.g. DATABASE_URL points at postgres:5432.)

The following are not in the current docker-compose.yml server env block and therefore won’t reach the container unless you add them (or set the credential-style ones in Settings → Credentials):

  • SMTP_* (Email node)
  • OPENAI_API_KEY, PERPLEXITY_API_KEY
  • RONDOFLOW_ADMIN_* (first-admin bootstrap)
  • CLAUDE_CODE_MAX_OUTPUT_TOKENS, MAX_CONCURRENT_AGENTS
  • RONDOFLOW_SPAWN_IDLE_TIMEOUT_MS, RONDOFLOW_SPAWN_MAX_MS
  • RONDOFLOW_TEARDOWN_ON_DISCONNECT, RONDOFLOW_TEARDOWN_GRACE_MS
  • RONDOFLOW_DEBUG_SPAWN, IS_SANDBOX
  • rondoflow_SECRET

The ui service separately sets NEXT_PUBLIC_API_URL, NEXT_PUBLIC_SOCKET_URL, and the DOCS_ORIGIN build arg.

Last updated on