Environment variables
None of these are required. The app runs with zero configuration — make setup && make start, or docker compose up, both work with no .env file.
AI provider + key, auth (password), email ingestion, Telegram, and every other connector are
configured from the running app’s Settings UI, not environment variables. Most variables
below exist as an alternative seed path for a scripted or headless deploy (a
docker-compose.yml, a provisioning script) that wants a value present on first boot; once a
store has a value, the UI is authoritative and the variable stops being read.
.env.example in the repository lists only the small set the app actually needs to boot
(data/cache/log locations, host, port, CORS) — by design it leaves out AI, auth, and connector
variables, since those are meant to be set from the UI. Every variable below that isn’t in that
file is still read as a one-time seed; its own store module is the authoritative source for it.
Where things live
| Variable | Default | What it does |
|---|---|---|
DATA_DIR | ~/.open-net-worth/data | Your ledger, accounts — everything money. |
CACHE_DIR | ~/.open-net-worth/cache | Price/news caches — safe to delete, regenerates. |
LOGS_DIR | ~/.open-net-worth/logs | Rotating app logs. |
ONW_DATABASE_URL | sqlite:///<DATA_DIR>/onw.db | Point at Postgres instead (e.g. postgresql+psycopg://user:pass@host/db). |
Server
| Variable | Default | What it does |
|---|---|---|
HOST | 127.0.0.1 | 0.0.0.0 to listen on your LAN (Docker sets this for you). |
PORT | 8080 | Port the backend listens on. |
CORS_ORIGINS | the local dev origins | Comma-separated allowed origins; * allows any. |
Auth (instance password)
On by default, normally set up from the UI on first run. These let a scripted deploy seed it instead:
| Variable | Default | What it does |
|---|---|---|
AUTH_DISABLED | false | true = no login at all. Dev only — never expose this publicly. |
AUTH_TRUST_LOOPBACK | true | false = require login even from localhost. |
AUTH_PASSWORD | unset | Plaintext seed, hashed into storage on first boot. |
AUTH_PASSWORD_HASH | unset | Pre-hashed (pbkdf2$...) — preferred over AUTH_PASSWORD, used as-is. |
AUTH_API_TOKEN | generated | Bearer token for scripted/API access. |
AUTH_SESSION_KEY | generated | HMAC session-signing key. |
AUTH_SESSION_TTL | 2592000 (30 days) | Login session lifetime, in seconds. |
AI provider (Lucius)
Normally set from Settings → AI Model. These seed the same config store for a headless
deploy: AI_PROVIDER, AI_MODEL, AI_MEMORY_MODEL (a cheaper model for background memory
extraction), ANTHROPIC_API_KEY, OPENAI_BASE_URL / OPENAI_API_BASE (for an
OpenAI-compatible endpoint — LiteLLM, Ollama, etc.).
Connectors (Telegram, email, banks, search, notifications)
Every connector is normally set up from Settings → Connectors, including any credential.
Per-connector environment variables (TELEGRAM_BOT_TOKEN, EMAIL_INGEST_ENABLED,
EMAIL_IMAP_PASSWORD, PLAID_CLIENT_ID, SEARCH_PROVIDER, and similar ones — none of them
listed in .env.example, which deliberately keeps to the vars needed just to boot) only seed
that store the first time it’s read with no saved row yet; once configured, the UI value wins
and the variable stops being read.
Advanced / internal
Tuning knobs most installs never touch: ONW_TURN_TIMEOUT_S (wall-clock cap on one AI turn,
default 1200 seconds), ONW_MIGRATE_ASSUME_BACKUP (skip the pre-migrate backup on a non-SQLite
database — don’t set this casually), and a handful of CLI-transport internals for the
Claude Code CLI / Codex CLI provider paths.
Not something you set
ONW_QA_MODE and its siblings exist purely for the project’s own automated testing — they make
the app refuse to start against your real data directory, a safety rail for test runs. No
purpose in normal use.