--- type: operations title: Configuration and data files description: How src/config.rs loads config.toml, the supported keys and defaults, 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-1b05507d7085e8e99a9b64f1 resource: repo://src/ai.rs - id: openwiki-source-2a737474d86fc75cc9d9694f resource: repo://src/config.rs generated: { by: "claude-code", at: "2026-10-19T19:53:56.480Z" } verified: - by: openwiki/0.7.2 at: 2026-10-19T19:53:56.480Z --- # Configuration and data files All runtime settings come from one TOML file loaded by `Config::load` (`src/config.rs:226-230`). No environment variable is ever read (`src/config.rs:1-20`). ## Data directory The data directory is fixed: `/wh`, resolved with `dirs::config_dir()` (`src/config.rs:144-148`). 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:258-261`). | File | Purpose | Defined by | |---|---|---| | `config.toml` | Optional settings | `CONFIG_FILE`, `src/config.rs:49` | | `session.db` | WhatsApp session store used by `whatsapp-rust` ([WhatsApp client](../integrations/whatsapp-client.md)) | `session_db_path`, `src/config.rs:267-269` | | `history.db` | Local messages, chats, contacts and autoreply flags ([History store](../concepts/history-store.md)) | `history_db_path`, `src/config.rs:275-277` | | `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:283-285` | 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` | Mapping and defaults: `src/config.rs:236-252`, `src/config.rs:29-38`. The three timing keys are unsigned integers in the file (`Option`, `src/config.rs:104-111`), so a negative value is rejected as an invalid value type; they are stored as `i64` in `Config`. Their use is described in [AI autoreply](../workflows/ai-autoreply.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. ## Loading rules - Missing file → all defaults (`src/config.rs:180-190`). - 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 (`src/config.rs:165-174`). This also covers negative or non-integer timing keys and a non-boolean `ai_log`. - 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:212-219`). 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:114-138`), and the raw `FileConfig` has no `Debug` impl at all (`src/config.rs:90-112`). 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, and it is only appended to, never rotated. Enable `ai_log` only for diagnosis and delete the file afterwards. - 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 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:288+` 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 and catch-up defaults, custom values (including `0`) and rejection of negatives, unknown keys, BOM/CRLF, non-leaking parse errors, missing, existing, invalid and unreadable files. See [Unit tests](../testing/unit-tests.md).