返回 DeepSeek-Reasonix
CONFIG_PATHS.md
根目录 / docs / CONFIG_PATHS.md
1 # Configuration Paths
2
3 Starting with **Reasonix v1.8.1**, Reasonix uses one user-facing home directory
4 for global configuration and user-owned state. CLI and desktop share this
5 location.
6
7 ## Reasonix Home
8
9 | Platform | Reasonix home |
10 | --- | --- |
11 | macOS | `~/.reasonix` |
12 | Linux | `~/.reasonix` |
13 | Windows | `%APPDATA%\reasonix` |
14
15 Set `REASONIX_HOME` to override Reasonix home for tests, CI, or portable
16 installations. Normal users should not need it.
17
18 When `REASONIX_HOME` is set, the runtime is fully self-contained: all
19 configuration, state, cache, and data live under that directory tree. Legacy
20 migration, OS-home convention directory scanning, and all other fallback paths
21 are skipped so no data leaks in from a system-wide production install.
22
23 Advanced test and portable setups may set `REASONIX_STATE_HOME` to move runtime
24 state such as sessions, archives, and memory. It does not move global config or
25 provider credentials: those remain under `REASONIX_HOME`. If an older build wrote
26 provider keys to `REASONIX_STATE_HOME/.env`, Reasonix imports those keys
27 non-destructively when `<Reasonix home>/.env` is missing them.
28
29 ## What Lives There
30
31 | Data | Path |
32 | --- | --- |
33 | Global config | `<Reasonix home>/config.toml` |
34 | Global provider credentials | `<Reasonix home>/.env` |
35 | In-progress model credential commits | `<Reasonix home>/transactions/model-credentials/` |
36 | Completed model settings receipts | `<Reasonix home>/transactions/model-settings-receipts/` |
37 | Legacy credentials import source | `<Reasonix home>/credentials` |
38 | Global slash commands | `<Reasonix home>/commands/` |
39 | Global skills | `<Reasonix home>/skills/` |
40 | Global hooks | `<Reasonix home>/settings.json` |
41 | Remote-SSH managed known_hosts | `<Reasonix home>/remote/known_hosts` |
42 | Sessions | `<state root>/sessions/` |
43 | Archives | `<state root>/archive/` |
44 | Memory | `<state root>/memory/` and `<state root>/projects/` |
45 | Global Desktop topic metadata | `<state root>/desktop/topic-state-v1.sqlite` |
46 | Project Desktop topic metadata | `<state root>/projects/<workspace slug>/desktop/topic-state-v1.sqlite` |
47 | Disposable session catalog | `<cache root>/session-catalog/v6.sqlite` |
48 | Disposable history search catalog | `<cache root>/history-search/v1.sqlite` |
49 | Disposable usage catalog | `<cache root>/usage-catalog/v1.sqlite` |
50 | Disposable task catalog | `<cache root>/task-catalog/v1.sqlite` |
51
52 `<state root>` defaults to `<Reasonix home>`. It only differs when
53 `REASONIX_STATE_HOME` is set.
54
55 Desktop detects a project-directory name collision when it saves a newly added
56 project in `desktop-projects.json`. A new assignment is made only when another
57 recorded project still resolves to the same legacy directory. Existing projects
58 and projects imported from older workspace records keep their current
59 `<state root>/projects/<workspace slug>/` directory. Re-adding the original
60 project after its colliding peer was assigned elsewhere also keeps that legacy
61 directory. Only a newly added project that meets the collision rule uses
62 `<state root>/projects/@<SHA-256 of its absolute root>/`; its `.workspace-root`
63 file records the assignment. Session, topic, and project-memory paths follow
64 that assignment. Listing a project never creates or changes an assignment, and
65 existing files are never moved. If two projects were already recorded with the
66 same slug before this fix, their historical shared files remain in place: the
67 old directory does not identify which project owns each file.
68 Studio currently resolves only `<state root>/projects/<workspace slug>/` and does
69 not read `.workspace-root`; a newly assigned project's state is therefore not
70 shared with Studio until Studio supports these assignments.
71
72 Desktop topic titles, title sources, creation times, and automatic-title state
73 are authoritative in these SQLite files. On first access, Desktop imports the
74 legacy `desktop-topic-*.json` files from a project's `.reasonix/` directory (or
75 the global Reasonix directory). A scope with legacy files continues mirroring
76 them for downgrade compatibility; a fresh scope does not create them. Legacy
77 files are retained, and project-local settings, skills, commands, attachments,
78 and `reasonix.toml` are unaffected.
79
80 The session catalog is a rebuildable query projection, not user data. Session
81 JSONL, event logs, metadata sidecars, and `desktop-projects.json` remain
82 authoritative. See [Session Catalog and Desktop Startup](./SESSION_CATALOG.md).
83 The history projection is documented in
84 [History Search Catalog](./HISTORY_SEARCH_CATALOG.md).
85 The usage rollup projection is documented in [Usage Catalog](./USAGE_CATALOG.md).
86 Task snapshots and event logs likewise remain authoritative; the rebuildable
87 cross-project projection is documented in [Task Catalog](./TASK_CATALOG.md).
88
89 The global user config is named `config.toml`. Project-local config files keep
90 the name `reasonix.toml`. If someone says "global reasonix.toml", they usually
91 mean `<Reasonix home>/config.toml`.
92
93 ## Global `config.toml`
94
95 `<Reasonix home>/config.toml` stores non-secret configuration shared by the CLI
96 and desktop app. It may contain the same provider, plugin, UI, desktop, tool,
97 skill, sandbox, bot, and agent settings that Reasonix renders into user config.
98 Provider entries store the name of the credential variable in `api_key_env`, not
99 the secret value.
100
101 Saved provider and bot credential variables are removed from every
102 model-controlled child-process environment. On macOS and Linux, the global
103 credential `.env` is also hidden from Reasonix's file readers, sandboxed shell
104 commands, and MCP servers; this does not change the visibility of a project's
105 ordinary `.env`. Windows has no OS-level shell sandbox: shell commands and
106 local tools run as the same OS user and can deliberately read user-readable
107 files, including the credential store, so treat restricted permissions there
108 as a tool-layer write boundary rather than a credential vault.
109
110 If a deny entry left behind by the retired Windows sandbox (v1.38.8 to
111 v1.38.10) blocks the credential store, Reasonix removes it automatically when
112 a marker from that sandbox run proves the entry came from Reasonix. Saving a
113 key works even without that proof: the save resets the file's ACL to the
114 current user without reading it, and if that is also denied it moves the
115 locked file aside as `.env.locked-<timestamp>` (a read deny does not block
116 the move) and writes a new store, so re-entering a key always succeeds. Plain reads never rewrite ACLs; they report
117 the original access error together with the repair outcome.
118
119 Example:
120
121 ```toml
122 config_version = 11
123 default_model = "deepseek/deepseek-flash"
124 language = "zh"
125 credentials_store = "auto" # legacy compatibility; provider keys are in .env
126
127 [ui]
128 theme = "auto"
129 cursor_shape = "bar" # CLI/TUI text cursor: underline|block|bar
130 show_turn_usage = false # hide per-request token/cost receipts in the TUI; default true
131
132 [desktop]
133 provider_access = ["deepseek"]
134
135 [[providers]]
136 name = "deepseek"
137 kind = "openai"
138 base_url = "https://api.deepseek.com"
139 models = ["deepseek-flash", "deepseek-v4-pro"]
140 default = "deepseek-flash"
141 api_key_env = "DEEPSEEK_API_KEY"
142 web_search = true
143
144 [[plugins]]
145 name = "example"
146 command = "example-mcp-server"
147 ```
148
149 Do not put API key values in `config.toml`. This file is regular configuration:
150 it is safe to inspect, edit, migrate, and include in diagnostics after standard
151 redaction. Secrets belong in the global `.env` below.
152
153 `[ui].cursor_shape` affects only the CLI/TUI composer. The default `bar` stays
154 visible without covering double-width CJK characters; use `block` or
155 `underline` if you prefer those cursor shapes.
156
157 `[ui].show_turn_usage = false` hides the token and cost receipt appended to the
158 TUI transcript after each model request. Accounting and live status updates
159 remain active. The default is `true`.
160
161 ### Custom provider `api_key_env` names
162
163 When a provider credential is added, replaced, or explicitly cleared from
164 desktop settings, TUI `/setup`, or `reasonix setup`, Reasonix allocates a fresh
165 `REASONIX_CONNECTION_*_KEY` slot. It writes that slot first and atomically
166 publishes the selected provider's new `api_key_env` reference second. Other
167 providers keep their current references, even when they previously shared a
168 fixed variable. Existing fixed names remain readable and are not migrated at
169 startup.
170
171 Legacy and manually authored provider entries may derive a default from the provider name. Names that normalize to
172 ASCII keep readable env names such as `LOCAL_GATEWAY_API_KEY`; names made
173 entirely of non-ASCII characters get a stable hash suffix such as
174 `CUSTOM_d39b9067_API_KEY` so two Chinese provider names do not share
175 `CUSTOM_API_KEY`. Names beginning with a digit get a `CUSTOM_` prefix so the
176 generated environment variable remains valid; for example, `9router` becomes
177 `CUSTOM_9ROUTER_API_KEY`.
178
179 The CLI custom-provider wizard uses this rule for its draft name. For example
180 `https://token.sensenova.cn/v1` creates provider name
181 `custom-token-sensenova-cn`, whose draft key env is
182 `CUSTOM_TOKEN_SENSENOVA_CN_API_KEY`. Pressing Enter at the variable-name
183 prompt keeps that draft only until the key is saved; the saved connection then
184 uses a newly allocated private slot.
185
186 A variable name you type at that prompt in `reasonix setup` is kept, so scripts
187 can refer to a stable name, as long as saving under it changes nothing another
188 connection reads: no other provider, bot or remote-host setting in the config
189 reads it, the global `.env` holds no value (or cleared marker) for it, and the
190 environment Reasonix runs in does not already set it. Otherwise the wizard says
191 what holds the name and asks again; Enter falls back to a
192 private slot. If the name is claimed between the prompt and saving, the save is
193 refused and nothing is written.
194
195 Saving a new key later for a provider in the user config rewrites its
196 variable in place when that provider (or the set of providers the key is saved
197 for) is the only reader of it in the user config, both before and after the
198 edit, and the global `.env` already holds its value. A project that reads the
199 same name sees the new key, as it saw the old one. Providers declared in a
200 project `reasonix.toml` always get a private slot. The previous value is kept in the global `.env` under
201 a temporary variable until the config is published: a save that fails or is
202 interrupted puts it back, unless something else has written the variable since.
203 When another provider or setting also reads the variable, the new key goes to a
204 private slot as before and the shared variable is left unchanged.
205
206 Existing configs are not rewritten on upgrade. If an old custom provider already
207 uses `CUSTOM_API_KEY`, it will keep working with that key. If several old custom
208 providers accidentally share `CUSTOM_API_KEY`, save each provider's API key
209 again to rotate that connection to a private slot.
210
211 ### Custom provider endpoint URLs
212
213 The desktop custom-provider form treats its **API address** as the exact request
214 URL and stores it in `request_url`; Reasonix does not append or rewrite its path.
215 Existing TOML entries are not reinterpreted: legacy `chat_url` keeps its former
216 OpenAI-only behavior, while Anthropic and Responses continue deriving their path
217 from `base_url` until the provider is explicitly saved in the current desktop UI.
218 Saving an OpenAI-compatible provider mirrors the exact address into legacy
219 `chat_url`, so previous releases continue using the same target. Previous
220 releases cannot honor arbitrary Anthropic or Responses request paths.
221 If model discovery needs a separate address, set `models_url`; otherwise Reasonix
222 probes candidates derived from `base_url`.
223
224 If a gateway requires vendor-specific top-level request body fields, set
225 `extra_body`, for example `extra_body = { enable_thinking = true }`. These values
226 are merged into the OpenAI-compatible chat JSON request body without allowing
227 core fields such as `model`, `messages`, `tools`, or `stream` to be overridden.
228
229 ## Global `.env`
230
231 `<Reasonix home>/.env` is the single runtime source for provider API keys saved
232 by Reasonix. The setup wizard, desktop settings, CLI missing-key prompts, and
233 provider-key delete actions all read or write this file through the same
234 credential helpers.
235
236 Structure:
237
238 ```dotenv
239 DEEPSEEK_API_KEY=sk-...
240 GEMINI_API_KEY=...
241 ANTHROPIC_API_KEY=...
242 # reasonix-cleared OLD_API_KEY
243 ```
244
245 Rules:
246
247 - one `KEY=value` assignment per line;
248 - blank lines and `#` comments are ignored;
249 - `export KEY=value` and quoted values are accepted when reading;
250 - multiline values are rejected by Reasonix writes;
251 - keys must use shell-style names such as `DEEPSEEK_API_KEY`;
252 - `# reasonix-cleared KEY` comments are non-secret tombstones written after a key
253 is deleted so legacy stores do not silently re-import it;
254 - Reasonix writes this file with restricted permissions where the OS supports
255 them.
256
257 For provider requests, Reasonix resolves only this global `.env`. Project `.env`
258 files, home `.env` files, inherited shell environment variables, the old
259 `credentials` file, and the OS keyring do not act as runtime provider-key
260 fallbacks. Project `.env`, home `.env`, and inherited shell environment values
261 are not imported into the global credentials file. The old `credentials` file
262 and old keyring entries are read only as non-destructive migration sources when
263 the new global `.env` is missing a key. Project `.env` files are still read as
264 workspace-scoped, non-provider expansion sources for `${VAR}` references in
265 MCP/plugin env, headers, URLs, commands, and args; those values are not written
266 into the process environment, and Reasonix control variables such as
267 `REASONIX_HOME`, `REASONIX_STATE_HOME`, and `XDG_CONFIG_HOME` are ignored there.
268
269 Caches remain in the OS cache directory, for example
270 `~/Library/Caches/reasonix` on macOS, `$XDG_CACHE_HOME/reasonix` or
271 `~/.cache/reasonix` on Linux, and `%LOCALAPPDATA%\reasonix\cache` on Windows.
272 Set `REASONIX_CACHE_HOME` to override the cache root. When `REASONIX_HOME` is
273 set, the cache is placed under `$REASONIX_HOME/cache` (unless
274 `REASONIX_CACHE_HOME` is also set, which takes precedence).
275
276 ## Config Priority
277
278 Runtime configuration is resolved in this order:
279
280 ```text
281 command-line flags
282 > project ./reasonix.toml
283 > global <Reasonix home>/config.toml
284 > compatible legacy global config
285 > built-in defaults
286 ```
287
288 Writes always target the new global path:
289
290 ```text
291 macOS/Linux: ~/.reasonix/config.toml
292 Windows: %APPDATA%\reasonix\config.toml
293 ```
294
295 ## Legacy Migration
296
297 Starting with **v1.8.1**, Reasonix automatically checks legacy locations on
298 startup before the first config load. Migration is synchronous, one-time, and
299 non-destructive: old files are copied or converted to Reasonix home and left
300 untouched.
301
302 Legacy config sources include:
303
304 ```text
305 ~/Library/Application Support/reasonix/config.toml
306 ~/.config/reasonix/config.toml
307 ~/.reasonix/reasonix.toml
308 ~/.reasonix/config.json
309 ```
310
311 Legacy credentials, memory files, and sessions are also imported into Reasonix
312 home when the new destination does not already exist. Legacy provider keys are
313 copied into `<Reasonix home>/.env` only when that file does not already contain
314 the same key. If the new global config already exists, it wins and legacy config
315 files are only kept as compatibility fallbacks.
316
317 Starting in **v1.9.1**, Reasonix also backfills MCP servers from known legacy
318 paths, legacy `config.json`, desktop-registered projects, and restored tab
319 projects into the global `<Reasonix home>/config.toml`. Existing global
320 `[[plugins]]` entries win by name, so project or legacy entries never overwrite a
321 server the user already configured globally. Source files are left untouched, and
322 the backfill writes a one-time marker so a user-deleted global MCP server is not
323 recreated repeatedly from an old project config.
324
325 ## Manual Migration Rescue
326
327 If Reasonix has already created the new home directory but some legacy data was
328 not present yet, or if the desktop app was opened before the old paths were
329 available, run the migration rescue command from either frontend:
330
331 ```text
332 /migrate
333 ```
334
335 In the CLI TUI, type `/migrate` into the chat input. In the desktop app, type the
336 same command into the composer. The command prints progress notices while it:
337
338 1. checks legacy config and credentials,
339 2. scans known legacy memory locations,
340 3. scans known legacy session directories,
341 4. imports memory files and sessions that were not previously imported, and
342 5. prints a final summary.
343
344 If old v0.x sessions live outside the known legacy locations — for example a
345 Windows v0.52 install/data directory chosen during setup — pass that directory
346 explicitly:
347
348 ```text
349 /migrate --from "D:\OldReasonix"
350 ```
351
352 The explicit form imports sessions only. The path may be the old install
353 directory, a `.reasonix`/data directory, or the `sessions` directory itself;
354 Reasonix checks the common layouts below that root and uses a source-specific
355 marker, so a previous plain `/migrate` run does not hide the later import.
356
357 The rescue command is intentionally non-destructive. It does not overwrite an
358 existing `<Reasonix home>/config.toml`; if the new config already exists, copy
359 any missing legacy settings across by hand. It copies legacy memory files only
360 when the destination file is absent. It also respects session import markers, so
361 sessions that were already imported and later deleted by the user will not be
362 restored on a later `/migrate` run.
363
364 Version limits:
365
366 - Automatic migration starts in **v1.8.1**.
367 - `/migrate` is available only in Go-based Reasonix builds that include the
368 command. If Reasonix reports `unknown command`, upgrade first and rerun it.
369 - The command is not available in the legacy `0.x` TypeScript line.
370 - Plain `/migrate` rescans the legacy locations listed above. Use
371 `/migrate --from <path>` only for a known v0.x session source; it is not a
372 backup restore tool or a downgrade importer.
373
373 lines MARKDOWN