Application Directories
July 23, 2026 · View on GitHub
LLxprt Code stores its files in OS-standard, platform-appropriate locations organized into four categories. This page is the canonical reference for where files live on Linux, macOS, and Windows, how to override those locations, and how the legacy ~/.llxprt layout is handled during migration.
If you only read one thing, read the Quick reference table. Everything else is a deeper explanation of those four rows.
Why this matters
Older versions of LLxprt Code kept every file under ~/.llxprt. That works on Linux but is wrong elsewhere: macOS users expect preferences in ~/Library/Preferences, Windows users expect app data under %LOCALAPPDATA%, and none of those users expect secrets sitting next to editable config. The current layout follows the conventions each operating system already defines (the same conventions your browser, editor, and shell use), so backups, disk-cleanup tools, and IT policies do the right thing automatically.
The migration is automatic: the first time you start a new version, LLxprt copies your existing ~/.llxprt files into their new homes. You do not need to move anything by hand.
Quick reference
LLxprt splits its files across four categories. Each category has an environment variable override and a per-platform default.
| Category | Holds | Override (env var) | Fallback override |
|---|---|---|---|
| Config | User-editable settings, profiles, prompts, commands, skills, policies, sandbox profiles, subagents, hooks config, global memory, .env | LLXPRT_CONFIG_HOME | (none — uses platform default) |
| Data | App-managed durable data: credentials/accounts, OAuth token fallback, conversations, history, todos, user extensions, secure store | LLXPRT_DATA_HOME | LLXPRT_CONFIG_HOME |
| Cache | Disposable caches and dumps — safe to delete at any time | LLXPRT_CACHE_HOME | LLXPRT_CONFIG_HOME |
| Log/state | Logs, debug output, temp dirs, checkpoints, shell history, OAuth advisory locks, OTEL telemetry artifacts | LLXPRT_LOG_HOME | LLXPRT_CONFIG_HOME |
The data, cache, and log/state categories all fall back to LLXPRT_CONFIG_HOME when their own override is unset. This keeps tests and single-root deployments simple: setting LLXPRT_CONFIG_HOME alone redirects everything.
Platform defaults
| Category | Linux | macOS | Windows |
|---|---|---|---|
| Config | ~/.config/llxprt-code | ~/Library/Preferences/llxprt-code | %APPDATA%\llxprt-code\Config |
| Data | ~/.local/share/llxprt-code | ~/Library/Application Support/llxprt-code | %LOCALAPPDATA%\llxprt-code\Data |
| Cache | ~/.cache/llxprt-code | ~/Library/Caches/llxprt-code | %LOCALAPPDATA%\llxprt-code\Cache |
| Log/state | ~/.local/state/llxprt-code | ~/Library/Logs/llxprt-code | %LOCALAPPDATA%\llxprt-code\Log |
Windows note: Data, cache, and log/state live under
%LOCALAPPDATA%(machine-local), not%APPDATA%(roaming). Credentials and logs should not roam between machines. The config category is the one exception — it lives under%APPDATA%(roaming) so your preferences follow you across machines, as is conventional for Windows applications.
Finding your effective paths
Because the effective location depends on both your platform and any environment variables you've set, prefer discovering the path at runtime rather than hard-coding it. From a shell:
# Print all four category defaults for this platform (ignores overrides)
node -e "import('env-paths').then(m => console.log(m.default('llxprt-code', { suffix: '' })))"
# Print the config directory, honoring LLXPRT_CONFIG_HOME
LLXPRT_CONFIG_HOME="${LLXPRT_CONFIG_HOME:-}" \
node -e "console.log(process.env.LLXPRT_CONFIG_HOME || (await import('env-paths')).default('llxprt-code', { suffix: '' }).config)"
If you're documenting a path inline and need a shorthand, use the category names: <config>, <data>, <cache>, and <log>. This page is the single place those shorthands are defined.
What lives in each category
Config — things you edit
The config category holds files you are expected to open in a text editor. LLxprt will never overwrite these without your action.
| Path | What it is |
|---|---|
<config>/settings.json | User-level settings (themes, model defaults, MCP servers, hooks, etc.) |
<config>/profiles/ | Saved provider/model profiles (/profile save) |
<config>/prompts/ | Prompt overrides and installed prompt packs |
<config>/commands/ | Custom slash commands (/commands authoring) |
<config>/skills/ | Personal agent skills (see Skills) |
<config>/policies/ | TOML policy files (see Policy Configuration) |
<config>/sandboxes/ | Sandbox profiles (see Sandbox Profiles) |
<config>/subagents/ | Subagent definitions (see Subagents) |
<config>/LLXPRT.md | Global memory / context file |
<config>/.LLXPRT_SYSTEM | Global system memory (added programmatically via /memory add core.global) |
<config>/.env | User-level environment file loaded at startup |
Hooks defined in
settings.jsonalways live in config (becausesettings.jsondoes). Hook scripts that those hooks invoke can live anywhere on your system — their path is whatever you put in thecommandfield.
Data — things LLxprt manages
The data category holds durable files LLxprt writes and reads on your behalf. You usually don't edit these directly.
| Path | What it is |
|---|---|
<data>/provider_accounts.json | Provider account registry |
<data>/google_accounts.json | Google account registry |
<data>/mcp-oauth-tokens.json | MCP server OAuth tokens |
<data>/oauth_creds.json | Legacy OAuth credentials filename (migration input) |
<data>/secure-store/ | Encrypted fallback for secrets when the OS keyring is unavailable |
<data>/extensions/ | User-installed extensions |
<data>/providers/ | User-defined provider aliases (see Providers) |
<data>/conversations/ | Conversation logs (when conversation logging is enabled) |
<data>/history/<hash>/ | Per-project checkpoint shadow Git repository (used for undo/redo checkpoints, not shell history) |
<data>/installation_id | Anonymous installation identifier |
<data>/machine_secret | Secret used to encrypt the secure-store fallback |
Cache — safe to delete
The cache category holds regenerable data. Deleting it frees disk space without losing configuration or credentials.
| Path | What it is |
|---|---|
<cache>/cache/ | General-purpose cache |
<cache>/dumps/ | Context dumps (see Context Dumping) |
Log/state — ephemeral runtime artifacts
The log/state category holds logs, temp files, and short-lived runtime state.
| Path | What it is |
|---|---|
<log>/debug/ | Debug logs (LLXPRT_DEBUG) |
<log>/tmp/<projectHash>/ | Per-project temp directory |
<log>/tmp/<projectHash>/checkpoints/ | Undo/redo checkpoints |
<log>/tmp/<projectHash>/shell_history | Shell command history (per project) |
<log>/oauth/locks/ | OAuth refresh/advisory locks (contain no credentials) |
<log>/tmp/<projectHash>/otel/ | OpenTelemetry collector artifacts (when enabled) |
Credential and token storage
OAuth tokens and API keys use a layered storage model:
- OS keyring (primary) — macOS Keychain, GNOME Keyring / KWallet, or Windows Credential Vault, accessed via
@napi-rs/keyring. This is encrypted at rest and requires authentication to read. - Encrypted file fallback (secondary) — if the keyring is unavailable, tokens fall back to encrypted files under
<data>/secure-store/<service>/. These files are encrypted with a machine-local secret (<data>/machine_secret) and are not plain JSON. - OAuth advisory locks — short-lived, credential-free lock files under
<log>/oauth/locks/that coordinate concurrent refresh attempts. They contain no secrets. Stale locks left behind by a crashed process are reclaimed only when the owner's PID is verifiably dead (probed viaprocess.kill(pid, 0)); a malformed or tokenless lock carries no verifiable owner identity and is left in place (deferred to manual cleanup) as a conservative safety-over-availability choice. If you encounter a stuck lock, remove it manually only when no LLxprt process is running.
The legacy oauth_creds.json plaintext token files (under the old ~/.llxprt directory, or ~/.gemini/oauth_creds.json from Gemini CLI) are obsolete. They are read only as a one-time migration input; LLxprt never creates them during normal operation.
See OAuth Setup for the user-facing OAuth workflow.
Override precedence
Each category resolves its directory in this order:
- Its own environment variable (e.g.
LLXPRT_DATA_HOME). LLXPRT_CONFIG_HOME(for data, cache, and log/state only).- The platform default from
envPaths('llxprt-code').
Config has no fallback — only LLXPRT_CONFIG_HOME or the platform default.
All overrides must be absolute paths. Relative paths and empty strings are ignored, so an unset variable behaves the same as one set to "".
# Example: redirect everything to a single portable directory
export LLXPRT_CONFIG_HOME=/srv/llxprt-config
# LLXPRT_DATA_HOME, LLXPRT_CACHE_HOME, LLXPRT_LOG_HOME are unset,
# so all three fall back to /srv/llxprt-config.
# Example: keep config under XDG but move large data elsewhere
export LLXPRT_DATA_HOME=/big-disk/llxprt-data
Workspace-local .llxprt
A directory named .llxprt inside your project is always valid and is unrelated to the legacy global ~/.llxprt. Workspace-local files override their user-level counterparts and are intended to be committed to version control so a team shares commands, skills, hooks, and project memory.
Workspace paths are produced by new Storage(projectRoot).<method> and include:
| Path | Scope |
|---|---|
<project>/.llxprt/settings.json | Workspace settings (highest precedence) |
<project>/.llxprt/commands/ | Workspace custom commands |
<project>/.llxprt/skills/ | Workspace agent skills |
<project>/.llxprt/extensions/ | Workspace extensions |
<project>/.llxprt/LLXPRT.md | Workspace memory / context file |
These are not affected by LLXPRT_*_HOME overrides. They live relative to your project root, period.
Legacy migration
On startup, LLxprt checks for a legacy ~/.llxprt directory. If present and not previously migrated, it copies files into the correct categories above and writes a global one-time completion marker (.migration-complete.json) so the copy pass runs at most once. For global memory files (LLXPRT.md, .LLXPRT_SYSTEM), a separate reconciliation pass moves any copies the memory tool previously wrote into the data category into the canonical config category. This pass writes its own per-file completion marker (.memory-reconcile-complete.json) that records each reconciled filename's identity, so late-appearing or changed sources are still picked up while already-reconciled files are skipped.
Completion marker semantics
The two markers are distinct and serve different purposes:
| Marker | Location | Scope | Purpose |
|---|---|---|---|
.migration-complete.json | <data> | Global, one-time | Suppresses the entire legacy ~/.llxprt → canonical copy pass after the first successful run. Once written, the copy pass is skipped on every subsequent startup. |
.memory-reconcile-complete.json | <data> | Per-file identity | Records which default global memory filenames (LLXPRT.md, .LLXPRT_SYSTEM) have been reconciled from data to config. Unlike the global migration marker, this marker does NOT suppress a rescan: on every startup, the bounded set of data-side memory source paths is re-scanned, and any source whose per-file identity is not yet recorded (or whose content differs from its archived backup) is reconciled. This handles late-appearing or changed sources while skipping already-reconciled files. |
~/.llxprtis an input only. LLxprt reads from it during migration; it never writes new files there during normal operation.- Migration is idempotent per file. Re-running with a file's marker present (and identity unchanged) is a no-op for that file.
- Nothing is deleted. Your old files remain in place after migration; you can remove them manually once you've confirmed everything works.
- Override interaction. Migration is skipped entirely when
LLXPRT_CONFIG_HOMEis set, on the assumption that you've taken control of your layout. To migrate into a custom location, unset the override for one run, then re-set it.
If you want to import settings or memory from a legacy ~/.llxprt after migration has already completed (for example, you deleted the new files and want to re-copy), see Tips for Gemini CLI Users for a manual import technique.
System-level settings (administrators)
Settings and policies can also be placed in system-wide locations that apply to all users on the machine. These are outside the four-category user layout:
| Platform | System settings path | Override env var |
|---|---|---|
| Linux | /etc/llxprt-code/settings.json | LLXPRT_SYSTEM_SETTINGS_PATH |
| macOS | /Library/Application Support/LlxprtCode/settings.json | LLXPRT_SYSTEM_SETTINGS_PATH |
| Windows | C:\ProgramData\llxprt-code\settings.json | LLXPRT_SYSTEM_SETTINGS_PATH |
Both the CLI settings loader and the policy engine resolve the system settings
path through the single canonical Storage.getSystemSettingsPath() authority.
The system policies/ directory always lives beside the resolved settings
path.
A legacy alias LLXPRT_CODE_SYSTEM_SETTINGS_PATH is honored as a bounded
backward-compatibility fallback inside Storage when the canonical env var is
unset. Prefer LLXPRT_SYSTEM_SETTINGS_PATH for new deployments.
System policies live in a policies/ directory beside the system settings file.
See Enterprise Configuration for details.
Related
- Configuration — the user settings file and its precedence
- OAuth Setup — OAuth workflow and credential storage
- Memory — global vs. project memory files
- Policy Configuration — TOML policy file locations