Forge
markdowndeeb25a2
1<!-- English translation of adr/0028-user-settings-toml-localappdata-and-secrets.md. Canonical Russian: ../../adr/0028-user-settings-toml-localappdata-and-secrets.md -->
2
3# ADR 0028: User settings - `settings.toml`, directory `%LocalAppData%\CascadeIDE\`, secrets separately
4
5**Status:** Accepted · Implemented
6**Date:** 2026-04-08
7**Updated:** 2026-04-13 - LSP presets in `settings.toml` ([0040](0040-lsp-launch-line-settings-toml-presets-and-environment.md)). Details - [§ History](#adr0028-history).
8
9## Related ADRs
10
11| ADR | Role |
12|-----|------|
13| [0010](0010-ui-modes-toml-configuration.md) | TOML for **bundle modes** and **repository** `workspace.toml` is another layer, not to be confused with the user file |
14| [0013](0013-command-surface-and-discoverability.md) | `hotkeys.toml` next to `settings.toml` - intended, not necessarily implemented |
15| [0026](0026-markdown-preview-surfaces-and-placement.md) | part of the presets in **merged** `workspace.toml`; custom placement override - not just `settings.toml` |
16| [0027](0027-small-team-focus-vs-public-maturity.md) | predictable configuration paths |
17| [0029](0029-configuration-toml-canonical-ui-facade.md) | TOML as canon; UI - facade above the same file |
18| [0017](0017-multi-window-workspace-and-agent-surfaces.md) | `presentation` - first of all **`settings.toml`**, not a repo |
19| [0040](0040-lsp-launch-line-settings-toml-presets-and-environment.md) | C#/Markdown LSP: presets, optional `executable`/`arguments` keys, optional environment |
20
21### Implementation snapshot
22
23| Element | Meaning |
24|---------|----------|
25| Service | `SettingsService` |
26| Catalog | `%LocalAppData%\CascadeIDE\` |
27| Settings | `settings.toml` (Tomlyn, snake_case) |
28| Secrets | `ai-keys.toml` (separate from settings) |
29
30## Summary
31
32- Custom canon: **`%LocalAppData%\CascadeIDE\settings.toml`** (Tomlyn).
33- Secrets - **`ai-keys.toml`**, not in the main settings file.
34- Separate from the `UiModes/` bundle and merge `.cascade/workspace.toml` ([0010](0010-ui-modes-toml-configuration.md)).
35- LSP presets - [0040](0040-lsp-launch-line-settings-toml-presets-and-environment.md).
36
37---
38## Context
39
40The product already has **several layers** of configuration: spiked `UiModes/` bundle, merge with `.cascade/workspace.toml` in the open repository ([0010](0010-ui-modes-toml-configuration.md)), global user preferences (AI, MCP, LSP, panel visibility, UI locale, etc.). At the same time, there was no **separate ADR** that captures **user** channel (path on disk, format, what we put where) - the links are scattered across [0010](0010-ui-modes-toml-configuration.md), [0015](0015-editor-toml-syntax-highlighting.md), UX-docs.
41
42We need **one canon point**: where “my computer” is located, what is in TOML, what cannot be put in TOML, how it relates to the repository layer.
43
44---
45
46## Solution
47
48### 1. CascadeIDE User Data Directory
49
50- **Base path:** `%LocalAppData%\CascadeIDE\` (via `Environment.SpecialFolder.LocalApplicationData` + `CascadeIDE` segment).
51- The directory is **created** on the first call, if missing (`SettingsService.GetSettingsDirectory()`).
52- This directory is **not** the solution directory and **not** `AppContext.BaseDirectory`; it is **tied to the Windows user** and the installation of the application on the machine.
53
54### 2. Main settings file: `settings.toml`
55
56- **Full path:** `%LocalAppData%\CascadeIDE\settings.toml`.
57- **Model:** `CascadeIdeSettings` is the only source of truth for **set of fields** and meanings (AI providers, MCP, LSP C#/Markdown, Kroki, panel visibility, `UiMode`, UI locale, etc.).
58- **Serialization:** Tomlyn via `CascadeTomlSerializer`: keys in file - **snake_case**, C# properties - PascalCase (`TomlSerializerOptions` with `JsonNamingPolicy.SnakeCaseLower`).
59- **Loading:** when starting `SettingsService.Load()`; in case of a reading/parsing error - **default model** from the code, without crashing the IDE.
60- **Saving:** `SettingsService.Save(CascadeIdeSettings)` - overwriting the entire file; write errors are **swallowed** (current implementation policy is not to block the UI).
61
62**Before public release:** do not increase automatic migrations in `SettingsService` when renaming/transferring TOML keys - see subsection **"Before public release"** in the [root README](../../README.md#before-public-release). After the appearance of mass installations, a separate solution (file version, one-time migrator, changelog).
63
64### 3. Historically: JSON is no longer supported
65- Previously, the code had a one-time migration **`settings.json` → `settings.toml`**; As of this ADR, there are **no legacy settings supported**, migration branch **removed** from `SettingsService.Load()`.
66- **Canon** - only **`settings.toml`**; if there is no file, factory defaults from embedded **`Settings/defaults-settings.toml`** (EmbeddedResource), then user overlay merge. The custom **`settings.json`** in `%LocalAppData%\CascadeIDE\` is **not** readable (if the file is left manually, you need to transfer it to TOML yourself or delete it).
67
68### 4. Secrets: not in `settings.toml`
69
70- **API keys** (Anthropic, OpenAI, DeepSeek, etc.) are stored in **`%LocalAppData%\CascadeIDE\ai-keys.toml`**, separate `AiKeys` model, serialized via **`CascadeTomlSerializer`** (same as `settings.toml`: keys in the file - **snake_case**), `AiKeysStorage.Load` / `Save`.
71- **Reasons for separation:** do not reveal secrets in the same file, which is convenient to copy/display in logs; simpler backup policy and `.gitignore` for the user; corresponds to the direction [0013](0013-command-surface-and-discoverability.md) (do not mix everything in one “lump”). **TOML** format rather than separate JSON - single serialization stack with custom settings.
72- **Do not commit** `ai-keys.toml`; The developer documentation assumes only a local machine. The file **`ai-keys.json`** is not readable (if it remains manually, move it to TOML or delete it).
73
74### 5. What this ADR *doesn't* describe (explicit distinction from [0010](0010-ui-modes-toml-configuration.md))
75
76| Layer | Where | Destination |
77|------|-----|------------|
78| User | `%LocalAppData%\CascadeIDE\settings.toml` | **User** preferences on this machine (model `CascadeIdeSettings`). |
79| Application bundle | `UiModes/` next to exe | UI modes, index, **spike** `workspace.toml` bundle. |
80| Repository | `<solution>/.cascade/workspace.toml` | Team/project overlay on top of the bundle (merge in [0010](0010-ui-modes-toml-configuration.md)). |
81
82#### Name `workspace.toml`: one merge contract, not “two meanings” - and where the file is not
83
84- **Chrome and mode presets ([0010](0010-ui-modes-toml-configuration.md)):** one data model (`UiWorkspaceToml`), two **sources** of one file name - spiked **`UiModes/workspace.toml`** and when the solution is open, optionally **`<repo>/.cascade/workspace.toml`**. This is a **merge chain** (bundle → overlay repo), and not two different products with the same name.
85- **`%LocalAppData%\CascadeIDE\`:** there is no **`workspace.toml` file** and in the current canon **not intended** - global user settings live in **`settings.toml`**. A typical confusion when reading the docs: look for "user workspace" next to `settings.toml` under the same name.
86
87**Renaming** `workspace.toml` in a bundle/repo (for example in `ui-workspace.toml`) **we do not do** just for the sake of uniqueness of the name on disk: it will affect the assembly, merge, keys in [0026](0026-markdown-preview-surfaces-and-placement.md), examples and expectations “workspace = IDE layout”. If ever needed - **separate ADR** + migration of paths and bundle versions.
88
89The state tied to a **specific solution** (for example, the selected startup project for debugging: `Services/StartupProjectStore` → `<directory .sln>/.cascade-ide/startup-project.json`) **does not** lie in `%LocalAppData%\CascadeIDE\` - this is a separate channel "next to the repo", not global user settings.
90
91### 6. Future files in the same directory
92
93- **`hotkeys.toml`** - by intent [0013](0013-command-surface-and-discoverability.md), next to `settings.toml`, overrides only; prior to implementation, this ADR does not obligate their availability.
94- Layout by **physical displays** (`presentation` / `zone_screen_layout` and grammar tokens by [0017](0017-multi-window-workspace-and-agent-surfaces.md)) - **personal** layer, **primarily** fields in **`settings.toml`**, rather than a team commitment via the repository `workspace.toml` ([0017 § storage layer](0017-multi-window-workspace-and-agent-surfaces.md#adr0017-presentation-grammar)).
95- Additional user files (for example, window status by [0017](0017-multi-window-workspace-and-agent-surfaces.md)) - **separate ADR** when an agreement appears, with an explicit link to this directory or to a new key in `settings.toml`.
96
97---
98
99## Consequences
100- Documentation and agents: with the word “user settings” - the path to **`settings.toml`** and the model **`CascadeIdeSettings`**; for “API secrets” - **`ai-keys.toml`**.
101- New fields in user settings are added to **`CascadeIdeSettings`** + if necessary UI/saving; when the meaning of a layer changes, **update this ADR** or refer to a new one.
102- Tests can replace loading through existing mechanisms or model instances without writing to disk - without changing the canon of paths.
103
104---
105
106## Rejected alternatives
107
108- **Keep custom settings in JSON as primary format** - rejected: canon - **TOML**; automatic migration from `settings.json` is **removed** from the code in the absence of supported legacy profiles.
109- **Store API keys in `settings.toml`** - rejected: mixing secrets with portable/editable config and risk of leakage when exchanging files.
110- **Keep secrets in a separate JSON (`ai-keys.json`)** - rejected in favor of **`ai-keys.toml`**: same format with `settings.toml` and the same `CascadeTomlSerializer`; the separate file still isolates the secrets from the "regular" config.
111- **One ADR for all TOML** - rejected: [0010](0010-ui-modes-toml-configuration.md) remains about modes and merge; user file - separate path and content contract.
112- **Rename `workspace.toml` in the bundle/repo only because the name matches expectations** - rejected without separate ADR and migration (see §5.1).
113
114---
115
116## History of changes
117
118<a id="adr0028-history"></a>
119
120| Date | Change |
121|------|-----------|
122| 2026-04-08 | Canon: `settings.toml`, LocalAppData directory; `workspace.toml` in the merge layer ([0010](0010-ui-modes-toml-configuration.md)). |
123| 2026-04-08 | Removed `settings.json` → TOML migration; legacy is not supported. |
124| 2026-04-08 | Secrets: `ai-keys.toml` instead of JSON; There is no migration from JSON. |
125| 2026-04-11 | Before public release - without auto-migrate scheme ([README § Before public release](../../README.md#before-public-release)). |
126| 2026-04-11 | `presentation` → [0017](0017-multi-window-workspace-and-agent-surfaces.md). |
127| 2026-04-13 | LSP in `settings.toml` → [0040](0040-lsp-launch-line-settings-toml-presets-and-environment.md). |
View only · write via MCP/CIDE