--- type: operations title: Configuration and data files description: How src/config.rs loads config.toml, the supported keys and defaults (including ai_retries and web_port), the template file, where the data directory and databases live, and operational and security caveats. tags: [configuration, toml, data-directory, sqlite, security] sources: - id: openwiki-source-ea70eb6c045047448e446296 resource: repo://.gitignore - id: openwiki-source-0d5b861992295d2e1663735c resource: repo://config.toml.global.template - id: openwiki-source-1b05507d7085e8e99a9b64f1 resource: repo://src/ai.rs - id: openwiki-source-2a737474d86fc75cc9d9694f resource: repo://src/config.rs generated: { by: "claude-code", at: "2026-10-20T20:27:06.515Z" } verified: - by: openwiki/0.7.2 at: 2026-10-20T20:27:06.515Z --- # Configuration and data files All runtime settings come from one TOML file loaded by `Config::load` (`src/config.rs:259-263`). No environment variable is ever read (`src/config.rs:1-25`). ## Data directory The data directory is fixed: `/wh`, resolved with `dirs::config_dir()` (`src/config.rs:166-170`). On Windows this is `%APPDATA%\wh`. It cannot be changed from the config file. At startup `ensure_data_dir` creates it if missing (`src/config.rs:293-296`). | File | Purpose | Defined by | |---|---|---| | `config.toml` | Optional settings | `CONFIG_FILE`, `src/config.rs:56` | | `session.db` | WhatsApp session store used by `whatsapp-rust` ([WhatsApp client](../integrations/whatsapp-client.md)) | `session_db_path`, `src/config.rs:302-304` | | `history.db` | Local messages, chats, contacts, per-chat autoreply flags and the global autoreply setting ([History store](../concepts/history-store.md)) | `history_db_path`, `src/config.rs:310-312` | | `ai_requests.log` | Optional AI request log, written only when `ai_log = true` ([AI client](../integrations/ai-client.md)) | `ai_log_path`, `src/config.rs:318-320` | The two databases are separate by design: `session.db` belongs to the WhatsApp library (linked-device session and keys, schema owned by `whatsapp-rust`), while `history.db` belongs to the app (chats, messages, contacts, autoreply flags). ## Keys | Key | Purpose | Default when missing or blank | |---|---|---| | `openai_api_key` | Bearer token for the AI endpoint (trimmed) | none — the [AI client](../integrations/ai-client.md) cannot be built | | `openai_base_url` | Base URL of the OpenAI-compatible endpoint (trimmed, trailing `/` removed) | `https://ollama.com/v1` | | `openai_model` | Model for text replies (trimmed) | `deepseek-v4.1-flash` | | `openai_vision_model` | Model for image replies (trimmed) | none — image understanding disabled | | `system_prompt` | Persona/instructions for autoreplies, kept as written (not trimmed, multiline allowed) | none — AI not configured | | `ai_log` | Boolean; when `true` every request sent to the AI model is appended to `ai_requests.log` | `false` | | `cooldown_seconds` | Minimum seconds between autoreplies in a private chat or to a group message addressed to the user; `0` disables it | `30` | | `group_cooldown_seconds` | Same, for group messages not addressed to the user; `0` disables it | `300` | | `catch_up_seconds` | After a restart, the last unanswered message of each private chat is answered once if it is at most this old; `0` disables the catch-up | `7200` | | `ai_retries` | How many times a failed AI request is retried, waiting 2 s, 4 s, …; HTTP 4xx other than 429 is never retried; `0` disables retrying | `2` | | `web_port` | TCP port of the [web control panel](../integrations/web-control-panel.md) on `0.0.0.0`, no authentication; valid range 1–65535 | unset — panel disabled | Mapping and defaults: `Config::from_file` (`src/config.rs:269-287`) and the constants at `src/config.rs:34-45`. The timing keys and `ai_retries` are unsigned integers in the file (`Option`, `src/config.rs:121-128`), so a negative value is rejected as an invalid value type; the timing keys are stored as `i64` in `Config`. `web_port` is `Option` (`src/config.rs:129-131`), so negatives and values above 65535 fail TOML parsing, and `resolve_web_port` (`src/config.rs:243-252`) additionally rejects `0`. Their use is described in [AI autoreply](../workflows/ai-autoreply.md), [AI client](../integrations/ai-client.md) and [Web control panel](../integrations/web-control-panel.md). The binary embeds no prompt text: without `system_prompt` the AI is not configured and the status line says so (see [Application state](../architecture/app-state.md)). Unknown keys are ignored. `config.toml.global.template` in the repository root lists every key with its default and a comment (with `web_port` commented out); copy it to the data directory as `config.toml` and fill in `openai_api_key` and `system_prompt`. ## Loading rules - Missing file → all defaults (`read_config_file`, `src/config.rs:202-212`). - Unreadable file → `cannot read config file `. - Invalid TOML or wrong value type → `invalid config file ` with the cause `invalid TOML syntax or value type at line L, column C`; the error never echoes file content (`parse_config_file`, `src/config.rs:187-196`). This also covers negative or non-integer timing keys and `ai_retries`, a non-boolean `ai_log` and an out-of-range `web_port`. - A leading UTF-8 BOM (common with Windows editors) and CRLF line endings are tolerated. - `openai_base_url` consisting only of slashes is rejected with `openai_base_url in config.toml must not be empty or only slashes` (`src/config.rs:234-241`). - `web_port = 0` is rejected with `web_port in config.toml must be between 1 and 65535`. Startup aborts with `cannot load configuration` on any of these errors ([Runtime](../architecture/runtime-and-event-loop.md)). ## Security and operational notes - The API key is never printed: `Config`'s `Debug` shows `***` or `` (`src/config.rs:134-160`), and the raw `FileConfig` has no `Debug` impl at all (`src/config.rs:105-132`). The AI client redacts it too. - `ai_requests.log` never contains the API key and replaces image data with a size placeholder, but it does store the full request (system prompt and chat context) and the reply in clear text, one record per attempt including retries, and it is only appended to, never rotated. Enable `ai_log` only for diagnosis and delete the file afterwards. - `web_port` exposes an unauthenticated HTTP panel on every network interface: anyone who can reach the port can see chat names, toggle autoreplies and quit the bot. Set it only on a trusted network or firewall the port. A port already in use aborts startup with `cannot start web server on port `. - Keep `config.toml` in the data directory, outside the repository. `.gitignore` ignores a root-level `/config.toml` and `.env` (`.gitignore:4-6`), as well as SQLite journal/WAL/SHM files; note that the `*.db` pattern is commented out (`.gitignore:7`), so databases copied into the repo would not be ignored. - `session.db` holds the linked-device credentials: deleting it forces a new QR pairing. `history.db` can be deleted to reset local history and the per-chat and global autoreply flags; it is recreated on start. - Images downloaded for vision replies are kept in memory only and never written to the data directory. ## Tests `src/config.rs:323+` covers defaults, full files, blank values, multiline prompts, trimming, base URL normalisation and errors, `Debug` masking, vision model, DB and AI log paths, `ai_log`, cooldown, catch-up and `ai_retries` defaults, custom values (including `0`) and rejection of negatives, `web_port` (unset, valid, `0` and out-of-range rejected), unknown keys, BOM/CRLF, non-leaking parse errors, missing, existing, invalid and unreadable files. See [Unit tests](../testing/unit-tests.md).