Skip to main content

Precedence

suchi reads configuration from two layers, last wins:
  1. Config file (optional overlay — see below)
  2. Process environment variables
Env values always beat file values. If a knob is set in both places, the env wins — safe for docker-compose overrides where the file lives in an image layer and the env comes from the run command.

Config file (optional)

Recommended format: TOML. Explicit typing, no indentation surprises, no parse ambiguity. suchi also accepts HUML, YAML, and JSON — but TOML is what the examples use. Format is picked from file extension:
Why TOML over YAML: YAML has the Norway problem (bare no parses as boolean false), sexagesimal time gotchas, and 1.10-decimal-vs- string ambiguity. TOML has none of these. If you use YAML anyway, quote string values to defuse the surprises.

Search paths

First readable file wins:
  1. $SUCHI_CONFIG — explicit path override (highest priority)
  2. $XDG_CONFIG_HOME/suchi/config.<ext>
  3. $HOME/.config/suchi/config.<ext>
  4. /etc/suchi/config.<ext>
  5. ./suchi.<ext> (cwd)
Within each directory, extensions are probed in the order toml → huml → yaml/yml → json.

File shape

Keys mirror env var names in lower_snake. PUBLIC_URL becomes public_url; BODY_LIMIT becomes body_limit. Nested tables flatten with _:
is equivalent to:
Arrays become CSV strings: ocr_languages = ["eng", "deu"] matches OCR_LANGUAGES=eng,deu. Full samples: SUCHI_CONFIG opts operator into a specific file bypassing the search order — useful for multi-environment deployments where prod and staging point at different TOMLs.

Environment variables — the authoritative knob list

Required

Storage & runtime

TLS

If a reverse proxy terminates TLS (recommended), leave these unset. For proxy-less installs, set both:

Local auth

No config needed. On first boot with no admin user, suchi prints a one-time setup token to the log at WARN. POST it to /setup with an email + password to create the admin.

OIDC

All-or-nothing group. Set the whole set or none:
Any OIDC provider works: Pocket-ID, Authentik, Keycloak, Microsoft Entra, Google.

Filesystem-watch ingest (Phase 2)

Email ingest (IMAP polling)

How it works. Every poll interval (default 5 min), suchi opens the folder, UID SEARCH UNSEEN, fetches each message’s raw body, stores it in the CAS as message/rfc822, and enqueues a post-ingest job. core/pipeline/eml then fans out one child document per attachment. Successfully imported messages are marked \Seen (or moved to ProcessedFolder when configured). Dedup is by Message-ID — a doc with the same Message-ID under the same owner is skipped on re-polls. Safe across restarts + across transports (the same email that also arrived via mbsync + fs-watch won’t double-ingest). Bridge tip. If you’re pointing at Proton Bridge, use imap://protonmail-bridge:143 (Bridge doesn’t do TLS internally — docker network isolates it) and provide the Bridge-generated app password via INGEST_IMAP_PASSWORD_FILE=/run/secrets/imap-password.

LLM classifier (optional; egress on)

Off by default. Setting these turns the classifier on; non-local endpoints require the egress ack. The setup wizard writes these into the settings table so live-reload works without a restart. Settings win over env when set.

Mail-mbsync sidecar (optional wizard)

The mail-mbsync compose bundle in deploy/mail-mbsync/ pairs suchi with an IMAP-poll sidecar. Credentials are written into an env file that the sidecar reads.
The server-rendered wizard at /admin/mail-setup retired with the SPA promotion; /admin/mail-setup now redirects here. An in-app mail-settings panel is tracked (needs GET/PUT /api/admin/settings/mail
  • a connection-test endpoint) — until it lands, populate the env file below by hand or via your deploy tool.
The MCP subcommand acts as a client to a running suchi instance — these tell it how to reach the API. Flags override env.

Config file loader itself

Settings-only knobs (in-DB, not env-driven)

These live in the settings table and are set via the admin UI’s setup wizard rather than env vars. Settings win over env for the handful of keys that overlap (LLM, ingest fs-watch, OCR languages).

Secrets convention

Every secret env var has a _FILE variant. The file’s contents (trimmed of trailing newlines) becomes the value. Matches docker + k8s secret mounts. Secrets are never logged. Session key sits at 0600. Confirmed _FILE variants today:
  • OIDC_CLIENT_SECRET_FILE
  • INGEST_IMAP_PASSWORD_FILE
  • LLM_API_KEY_FILE
If both the plain and _FILE variants are set, _FILE wins.

Egress surface

A stock install makes zero outbound connections (principle 8). At boot, suchi logs main.egress.surface listing every configured outbound path — OIDC discovery, IMAP polling, cloud LLM endpoints, export sinks. outbound: none is the honest default.
See privacy for the full posture.