Appendix

July 16, 2026 · View on GitHub

Where lean-ctx stores everything, every environment variable that changes its behavior, and every config section. Source: rust/src/core/data_dir.rs, rust/src/core/config/.


1. Directories (XDG Base Directory layout)

Since GH #408 lean-ctx separates its files into the standard XDG categories so the config dir can be mounted read-only. Typed resolvers live in rust/src/core/paths.rs (config_dir(), data_dir(), state_dir(), cache_dir(), runtime_dir()).

CategoryDefaultOverrideContents
Config$XDG_CONFIG_HOME/lean-ctx (~/.config/lean-ctx)LEAN_CTX_CONFIG_DIRconfig.toml, env.sh, shell-hook.*RO-safe
Data$XDG_DATA_HOME/lean-ctx (~/.local/share/lean-ctx)LEAN_CTX_DATA_DIRsessions/, vectors/, graphs/, knowledge/, archives/, memory/, packages/, agents/, stats.json, client-id.json
State$XDG_STATE_HOME/lean-ctx (~/.local/state/lean-ctx)LEAN_CTX_STATE_DIRevents.jsonl, journal.md, *.log, mcp-live.json, heatmap.json, pipeline_stats.json, cost_attribution.json, context_ledger.json, cooccurrence/, tee/, dashboard.token, agent_runtime_env.json (0600)
Cache$XDG_CACHE_HOME/lean-ctx (~/.cache/lean-ctx)LEAN_CTX_CACHE_DIRsemantic_cache/, models/, anomaly_detector.json, *_learned.json, litm_calibration.json, context_ir_v1.json, latest-version.json, .first_run_wow_done
Runtimedirs::data_local_dir()/lean-ctxdaemon.pid, daemon.sock, daemon-*.log, *.lock

Unix dir permissions: 0700. The Runtime dir is the OS data-local dir (~/.local/share/lean-ctx on Linux, ~/Library/Application Support/lean-ctx on macOS); it holds only daemon IPC and is intentionally separate from the data dir.

Resolution order (per category)

Each resolver applies the same three steps:

  1. Explicit overrideLEAN_CTX_<CATEGORY>_DIR set & non-empty → wins.
  2. Single-dir backward-compat — existing installs never split silently. If LEAN_CTX_DATA_DIR points at a non-standard path, or legacy ~/.lean-ctx holds data, or mixed $XDG_CONFIG_HOME/lean-ctx holds data (the pre-#408 layout), then all categories collapse onto that one directory — byte-for-byte the old behavior.
  3. XDG split default — a fresh install uses the per-category default above.

"Holds data" = contains a data marker (stats.json, sessions/, vectors/, graphs/, knowledge/). config.toml/hooks alone do not count, so a config-only dir does not pin the other categories back onto it.

Data-only pin (GH #594). A LEAN_CTX_DATA_DIR equal to the standard data dir ($XDG_DATA_HOME/lean-ctx) is treated as a data pin only and does not collapse config/state/cache. Older versions baked exactly that value into editor MCP env blocks; honoring it as single-dir made the MCP server read config.toml from the data dir while the CLI read it from the config dir. Now both keep the XDG split, so they always read the same config.

Don't hardcode LEAN_CTX_DATA_DIR in editor MCP configs — a custom path still forces the legacy single-dir layout. Leave it unset for the clean XDG split; existing installs are auto-detected and keep working, and setup / update strip any stale pin a previous version may have written.

Migrate an existing install to the split (opt-in)

Legacy/mixed installs keep working in single-dir mode. To adopt the four-dir layout on demand:

CommandEffect
lean-ctx doctorReports XDG layout: N item(s) in single dir when a split is available
lean-ctx doctor --fixMoves data/state/cache out of the config dir into their XDG homes

The migration is all-or-nothing (partial moves would re-collapse via back-compat), idempotent/resumable (existing destinations are skipped, never clobbered) and crash-safe (atomic rename, copy+remove fallback across filesystems; the source is removed only after a successful copy). An explicit LEAN_CTX_DATA_DIR is treated as a deliberate single-dir choice and is never auto-split; runtime files are left in place.

Read-only config sandbox (the #408 goal)

Once split, the config dir holds only config.toml + hooks, so it can be mounted read-only while the writable categories live elsewhere:

--ro    $XDG_CONFIG_HOME/lean-ctx     # config.toml, shell hooks
--rw    $XDG_DATA_HOME/lean-ctx       # sessions, vectors, graphs, knowledge
--rw    $XDG_STATE_HOME/lean-ctx      # events, journals, logs, ledgers
--tmpfs $XDG_CACHE_HOME/lean-ctx      # semantic cache, models (regenerable)
#       runtime (daemon.pid/sock) lives in the OS data-local dir

Project-local lean-ctx data (in the repo, not these dirs): .lean-ctx.toml (project config override), .lean-ctx-id, .lean-ctx/.


2. Environment variables

There are ~120 env vars; the ones you'll actually touch are below. The full list is in rust/src/core/config/. Most have a matching config.toml key — the env var always wins.

The ones you'll use

VariablePurposeDefault
LEAN_CTX_DISABLED=1Bypass ALL compression + disable shell hookunset
LEAN_CTX_RAW=1Uncompressed output for one commandunset
LEAN_CTX_DATA_DIRExplicit data dir; a custom path also forces legacy single-dir mode (a standard-path pin is data-only, see §1)$XDG_DATA_HOME/lean-ctx
LEAN_CTX_CONFIG_DIRExplicit config dir (config.toml, hooks)$XDG_CONFIG_HOME/lean-ctx
LEAN_CTX_STATE_DIRExplicit state dir (events, logs, ledgers)$XDG_STATE_HOME/lean-ctx
LEAN_CTX_CACHE_DIRExplicit cache dir (semantic cache, models)$XDG_CACHE_HOME/lean-ctx
LEAN_CTX_PROJECT_ROOTExplicit project rootauto-detected
LEAN_CTX_TOOL_PROFILEminimal|standard|powerconfig / power
LEAN_CTX_PROFILEActive context profileconfig / coder
LEAN_CTX_COMPRESSIONoff|lite|standard|maxconfig / lite
LEAN_CTX_RESEARCH_PROSE_CAPProxy research prose squeeze cap in bytes20000
LEAN_CTX_MEMORY_PROFILElow|balanced|performanceperformance
LEAN_CTX_PROXY_PORTProxy port4444
LEAN_CTX_NO_UPDATE_CHECK=1Disable update checkunset
LEAN_CTX_ALLOW_PATHExtra PathJail roots (path list; see §5)unset
LEAN_CTX_EXTRA_ROOTSMulti-root workspace roots (path list; see §5)unset

Provider tokens (for ctx_provider)

GITHUB_TOKEN / GH_TOKEN, GITLAB_TOKEN / CI_JOB_TOKEN, JIRA_URL + JIRA_EMAIL + JIRA_TOKEN, DATABASE_URL. Optional LLM enhance: OPENROUTER_API_KEY, ANTHROPIC_API_KEY.

Internal (set by lean-ctx itself — don't set these)

LEAN_CTX_MCP_SERVER, LEAN_CTX_ACTIVE, LEAN_CTX_HOOK_CHILD, LEAN_CTX_HEADLESS, LEAN_CTX_PLUGIN_DIR, etc.


3. Config file (config.toml)

Global at <CONFIG_DIR>/config.toml ($XDG_CONFIG_HOME/lean-ctx/config.toml, or the single dir for legacy/mixed installs — see §1); per-project override at <repo>/.lean-ctx.toml (merged, project wins). Manage with lean-ctx config (set, schema, validate, show).

Sections

SectionWhat it controls
(root keys)compression, cache, shell hook, profiles, memory caps, savings footer, proxy tri-state
[tools]profile (minimal/standard/power), explicit enabled list
[setup]auto_inject_rules, auto_inject_skills, auto_update_mcp
[archive]Zero-loss tool-output archive: enabled, threshold_chars (800), max_age_hours (48), max_disk_mb (500)
[search]BM25/dense/splade weights + candidate counts
[autonomy]Auto preload/dedup/consolidate, cognition loop
[providers]GitHub/GitLab/Jira/Postgres + MCP bridges
[loop_detection]Per-tool call limits to prevent agent loops
[updates]auto_update, check_interval_hours (6), notify_only
[boundary_policy]Cross-project search/import + universal gotchas
[secret_detection]Secret redaction in output
[cloud]contribute_enabled + sync timestamps
[proxy]Upstream URLs for Anthropic/OpenAI/Gemini
[memory.*]Knowledge/episodic/procedural/lifecycle/gotcha/embeddings caps
[llm]Optional local LLM enhance (Ollama)

Key defaults worth knowing:

  • compression_level = "lite" (root) — light compression on by default.
  • savings_footer = "always" config default, but the SavingsFooter enum default is Never so no inline footer tokens are emitted unless enabled.
  • memory_profile = "performance", memory_cleanup = "aggressive".
  • [memory.knowledge] max_facts = 200 — the source of doctor's "facts at capacity" warning.

4. Files written outside the lean-ctx dirs

CategoryExamplesWritten by
Shell hook~/.zshenv, ~/.bashenv, fish, PowerShell profilesetup step 1 / init --global
Agent aliases~/.zshrc, ~/.bashrc (lean-ctx-on/off/mode/status)setup / init --global
MCP configs~/.cursor/mcp.json, ~/.claude.json, ~30 editorssetup step 3 / init --agent
Agent rules (opt-in)~/.cursor/rules/lean-ctx.mdc, AGENTS.md blockssetup step 4
Skills (opt-in)~/.claude/skills/lean-ctx/, …setup step 6
Proxy env (opt-in)RC exports, ~/.claude/settings.json, Codex config.tomlproxy enable
Autostart~/Library/LaunchAgents/com.leanctx.{proxy,daemon,autoupdate}.plist; systemd user units on Linuxsetup steps 5/9
Binary~/.local/bin/lean-ctxinstaller / dev-install

Every edit to an existing file goes through config_io::write_atomic, which writes a *.lean-ctx.bak backup first. Rules injection only rewrites content between <!-- lean-ctx --> markers — your own content is preserved. lean-ctx uninstall reverses all of the above.


5. Filesystem boundary — path_jail, allow_paths, extra_roots (GH #392)

All tool file access (ctx_read, ctx_edit, ctx_tree, …) is jailed under the current project_root (PathJail). Three knobs widen or remove that boundary — they overlap, so here is exactly what each one does:

KnobEffectUse when
allow_paths = ["…"] (root key)Adds directories to PathJail's whitelist. Tools may read/edit under them, but ctx_tree/ctx_search do not scan them.One extra directory needs to be readable/editable (e.g. a shared skills folder).
extra_roots = ["…"] (root key)Same whitelist effect as allow_paths plus multi-root scanning: ctx_tree, ctx_search, overview treat them as additional project roots.Multi-repo workspaces; agent-harness memory/state dirs outside the project (e.g. Claude Code's ~/.claude/projects/<slug>/memory/).
path_jail = false (root key)Disables PathJail entirely — every absolute path is allowed.Sandboxed environments (bwrap, containers, VMs) where the OS is the boundary.
allow_ide_config_dirs = true (root key)Adds every supported editor's config dir to the read whitelist — registry-derived (~/.cursor, VS Code, Cline/Roo, JetBrains, …). Opt-in; exposes other agents' sessions/credentials.Letting the agent manage MCP setup across editors.

Env equivalents (path-list syntax, : on Unix / ; on Windows): LEAN_CTX_ALLOW_PATH (= allow_paths), LEAN_CTX_EXTRA_ROOTS (= extra_roots).

Notes that save debugging time:

  • ~, $VAR and ${VAR} are expanded in allow_paths / extra_roots / the env vars (since v3.8.1). On older versions "$HOME/code" was matched literally and silently never applied.
  • allow_paths = ["/"] technically whitelists everything; prefer the explicit path_jail = falselean-ctx doctor flags the "/" pattern.
  • Config changes are picked up on the next tool call (mtime-based reload); no MCP server restart needed. If a change appears to do nothing, run lean-ctx doctor: it reports config parse errors (a broken config.toml silently falls back to defaults) and dead allow_paths entries (unset $VAR, missing directory), plus the effective jail state.
  • Compile-time off-switch: building with the no-jail cargo feature removes the jail entirely (for trusted single-user builds).
  • Removed: the LEAN_CTX_NO_JAIL=1 env var (≤ 3.7.3). It was replaced by the path_jail = false config key and the no-jail compile feature; setting the old env var has no effect on current versions.
  • Home-level IDE config dirs are excluded from the jail's whitelist by default. Opt in with allow_ide_config_dirs = true (or LEAN_CTX_ALLOW_IDE_DIRS=1): the allow-list is derived from the editor registry, so it covers every supported editor — including non-dotfile layouts like VS Code (~/Library/Application Support/Code/User), Cline/Roo and JetBrains — and never drifts as editors are added. ~/.lean-ctx is always allowed, and a config file that lives directly in $HOME never widens the jail to the whole home directory. lean-ctx setup asks once (informed consent) and the relaxation is audited by lean-ctx doctor. These dirs expose other agents' sessions and credentials.