Skip to main content

Config

Struct Config 

Source
pub struct Config {
Show 121 fields pub ultra_compact: bool, pub tee_mode: TeeMode, pub recovery_hints: RecoveryHints, pub output_density: OutputDensity, pub checkpoint_interval: u32, pub excluded_commands: Vec<String>, pub passthrough_urls: Vec<String>, pub custom_aliases: Vec<AliasEntry>, pub preserve_compact_formats: Vec<String>, pub crush_verbatim_json: bool, pub slow_command_threshold_ms: u64, pub theme: String, pub cloud: CloudConfig, pub gain: GainConfig, pub cost: CostConfig, pub code_health: CodeHealthConfig, pub autonomy: AutonomyConfig, pub providers: ProvidersConfig, pub proxy: ProxyConfig, pub proxy_enabled: Option<bool>, pub proxy_port: Option<u16>, pub proxy_timeout_ms: Option<u64>, pub proxy_require_token: bool, pub proxy_loopback_open: bool, pub proxy_bind_host: Option<String>, pub proxy_allowed_hosts: Vec<String>, pub proxy_max_rps: Option<u32>, pub dashboard_auth: bool, pub buddy_enabled: bool, pub enable_wakeup_ctx: bool, pub redirect_exclude: Vec<String>, pub disabled_tools: Vec<String>, pub prefer_native_editor: bool, pub default_tool_categories: Vec<String>, pub no_degrade: bool, pub delta_explicit: bool, pub profile: Option<String>, pub tool_profile: Option<String>, pub tools_enabled: Vec<String>, pub persona: Option<String>, pub loop_detection: LoopDetectionConfig, pub rules_scope: Option<String>, pub rules_injection: Option<String>, pub permission_inheritance: Option<String>, pub extra_ignore_patterns: Vec<String>, pub terse_agent: TerseAgent, pub compression_level: CompressionLevel, pub compression_aggressiveness: Option<f64>, pub archive: ArchiveConfig, pub memory: MemoryPolicy, pub allow_paths: Vec<String>, pub allow_ide_config_dirs: Option<bool>, pub extra_roots: Vec<String>, pub read_only_roots: Vec<String>, pub allow_symlink_roots: Vec<String>, pub content_defined_chunking: bool, pub minimal_overhead: bool, pub symbol_map_auto: bool, pub structure_first: bool, pub auto_mode_learning: bool, pub team_url: Option<String>, pub team_token: Option<String>, pub team_auto_push: bool, pub journal_enabled: bool, pub auto_capture: bool, pub search: HybridConfig, pub graph: GraphConfig, pub index: IndexConfig, pub skillify: SkillifyConfig, pub summaries: SummariesConfig, pub llm: LlmConfig, pub embedding: EmbeddingConfig, pub shell_hook_disabled: bool, pub shadow_mode: bool, pub hook_mode: Option<String>, pub debug_log: bool, pub shell_activation: ShellActivation, pub skip_agent_aliases: bool, pub read_redirect: ReadRedirect, pub read_dedup: ReadDedup, pub update_check_disabled: bool, pub updates: UpdatesConfig, pub context: ContextConfig, pub bm25_max_cache_mb: u64, pub graph_index_max_files: u64, pub memory_profile: MemoryProfile, pub memory_cleanup: MemoryCleanup, pub max_ram_percent: u8, pub max_disk_mb: u64, pub max_staleness_days: u32, pub max_index_threads: usize, pub savings_footer: SavingsFooter, pub project_root: Option<String>, pub lsp: HashMap<String, String>, pub ide_paths: HashMap<String, Vec<String>>, pub model_context_windows: HashMap<String, usize>, pub response_verbosity: ResponseVerbosity, pub bypass_hints: Option<String>, pub cache_policy: Option<String>, pub cache_max_tokens: usize, pub boundary_policy: BoundaryPolicy, pub secret_detection: SecretDetectionConfig, pub sensitivity: SensitivityConfig, pub gateway: GatewayConfig, pub gateway_server: GatewayServerConfig, pub addons: AddonsConfig, pub allow_auto_reroot: bool, pub hook_binary: Option<String>, pub path_jail: Option<bool>, pub sandbox_level: u8, pub reference_results: bool, pub agent_token_budget: usize, pub shell_allowlist: Vec<String>, pub shell_allowlist_extra: Vec<String>, pub shell_strict_mode: bool, pub shell_security: Option<String>, pub shell_timeout_secs: Option<u64>, pub shell_heavy_timeout_secs: Option<u64>, pub shell_allow_writes: bool, pub shell_allow_inline_scripts: bool, pub setup: SetupConfig,
}
Expand description

Global lean-ctx configuration loaded from config.toml, merged with project-local overrides.

Fields§

§ultra_compact: bool§tee_mode: TeeMode§recovery_hints: RecoveryHints

Verbosity of the reactive recovery footer on compressed output (off|minimal|full, default minimal). See RecoveryHints.

§output_density: OutputDensity§checkpoint_interval: u32§excluded_commands: Vec<String>§passthrough_urls: Vec<String>§custom_aliases: Vec<AliasEntry>§preserve_compact_formats: Vec<String>

Output formats that are already compact/token-oriented and must be preserved verbatim instead of being recompressed (#342). Matched against the output shape (not the command name), so any tool emitting the format is covered without enumerating commands in excluded_commands. Default: ["toon"]. Set to [] to disable and always recompress.

§crush_verbatim_json: bool

Opt-in: apply the lossless JSON crusher to verbatim data commands (gh api, jq, kubectl get -o json, curl JSON). Off by default, so those outputs stay byte-for-byte verbatim. When on, an array-heavy JSON payload the crusher can at least halve is reshaped into a compact, fully reconstructible form; everything else stays verbatim. See Config::crush_verbatim_json_enabled (#936).

§slow_command_threshold_ms: u64

Commands taking longer than this threshold (ms) are recorded in the slow log. Set to 0 to disable slow logging.

§theme: String§cloud: CloudConfig§gain: GainConfig§cost: CostConfig

Model declaration for measured-vs-estimated cost reporting (MCP-only IDEs).

§code_health: CodeHealthConfig

Code-health engine: cognitive complexity, naming, coupling, edit-gate.

§autonomy: AutonomyConfig§providers: ProvidersConfig§proxy: ProxyConfig§proxy_enabled: Option<bool>

Whether the API proxy is enabled. Tri-state:

  • None: undecided (fresh install, will prompt on interactive setup)
  • Some(true): user opted in, proxy managed by lean-ctx
  • Some(false): user opted out, never touch proxy or endpoints
§proxy_port: Option<u16>§proxy_timeout_ms: Option<u64>

Proxy reachability timeout in milliseconds. Default: 200. Override via LEAN_CTX_PROXY_TIMEOUT_MS env var.

§proxy_require_token: bool

Strict proxy auth: when true, authenticate ONLY via the Bearer token (LEAN_CTX_PROXY_TOKEN) and disable the provider-API-key fallback. Default false keeps the loopback-friendly behavior where any local AI tool’s own provider key authenticates (the proxy never injects upstream credentials — it forwards the caller’s key verbatim). Enable on shared/multi-user hosts to require the token; clients must then send Authorization: Bearer <token>.

§proxy_loopback_open: bool

Skip ALL proxy authentication on loopback-bound listeners (#755). When true and the proxy binds a loopback address, every request is accepted without a Bearer token or provider API key — MCP clients, browser dashboards, and CLI tools all work without auth setup. Ignored on non-loopback binds (gateway mode always requires auth). Env override: LEAN_CTX_PROXY_LOOPBACK_OPEN.

§proxy_bind_host: Option<String>

Bind address for the proxy listener (gateway mode, enterprise#8). Default None = 127.0.0.1 — local-safe, nothing changes for existing installs. Set "0.0.0.0" (or a specific interface IP) to serve a whole org from one host; any non-loopback bind hard-disables the provider-key auth fallback (Bearer token becomes mandatory) and enables the proxy_allowed_hosts Host-header allowlist. Env override: LEAN_CTX_PROXY_BIND_HOST. An unparseable value falls back to loopback, never to an open bind.

§proxy_allowed_hosts: Vec<String>

Host-header allowlist for a non-loopback proxy bind (gateway mode): DNS-rebinding protection. Entries are hostnames or IPs without port (e.g. "gateway.example.com"). Loopback names are always allowed. Ignored (loopback-only guard, today’s behavior) while the bind is loopback. Empty + non-loopback bind = only loopback Host headers pass, so configure this when exposing the gateway.

§proxy_max_rps: Option<u32>

Proxy-wide request rate limit in requests/second (token bucket, burst = 2x). None (default) = unlimited on a loopback bind — today’s behavior — and 50 rps with burst 100 on a non-loopback bind (gateway mode ships a sane floor, enterprise#37). 0 disables the limiter even in gateway mode (explicit opt-out).

§dashboard_auth: bool

Require Bearer-token authentication for the dashboard. Default true: the dashboard generates (or uses the pinned) token and rejects /api/* and /metrics without it. Set to false to run the dashboard with no auth token — useful for a local/Docker setup where managing a token is inconvenient. No-auth mode is not unprotected: cross-origin and CSRF attacks from a malicious local website are blocked by request-header validation instead (Sec-Fetch-Site, Origin/Host same-origin, and a Host allowlist against DNS rebinding — see dashboard::no_auth_request_ok). Override per-run via the --no-auth / --auth=<bool> flag or the LEAN_CTX_DASHBOARD_AUTH env var.

§buddy_enabled: bool§enable_wakeup_ctx: bool§redirect_exclude: Vec<String>§disabled_tools: Vec<String>

Tools to exclude from the MCP tool list returned by list_tools. Accepts exact tool names (e.g. ["ctx_graph", "ctx_agent"]). Empty by default — all tools listed, no behaviour change.

§prefer_native_editor: bool

Prefer the host agent’s native editor over lean-ctx edit operations (#454). When true, the lean-ctx edit tool(s) (see EDIT_TOOL_NAMES) are neither advertised in list_tools nor dispatchable (direct or via ctx_call), so the agent falls back to the host’s built-in editing UI. Reads / search / shell / memory tools are unaffected. Override via LEAN_CTX_PREFER_NATIVE_EDITOR=1.

§default_tool_categories: Vec<String>

Tool categories to activate by default for dynamic-tool-capable clients. Values: “core” (always on), “arch”, “debug”, “memory”, “metrics”, “session”. Example: default_tool_categories = ["core", "arch", "memory"] Override via LCTX_DEFAULT_CATEGORIES env var (comma-separated). Empty = lean-ctx default (core + session).

§no_degrade: bool

Disable all automatic read-mode degradation (auto_degrade + context_gate pressure). When true, lean-ctx never downgrades requested read modes regardless of pressure. Override via LCTX_NO_DEGRADE=1 env var.

§delta_explicit: bool

Serve explicit full/lines:N-M re-reads of session-cached files as deltas: when the file changed on disk since it was cached, the read returns mode=diff instead of re-emitting content the model already holds. First reads are unaffected; fresh=true always bypasses. Opt-in. Override via LCTX_DELTA_EXPLICIT=1/0 env var.

§profile: Option<String>

Persistent profile name. Checked after LEAN_CTX_PROFILE env var. Set via lean-ctx config set profile passthrough or editing config.toml.

§tool_profile: Option<String>

Tool visibility profile: “minimal” (5), “standard” (15), or “power” (all). Override via LEAN_CTX_TOOL_PROFILE env var. Existing installs default to “power” (backward compat).

§tools_enabled: Vec<String>

Explicit list of enabled tool names. Used only when no tool_profile is pinned (tool_profile takes precedence); leave tool_profile unset to apply this list. The universal invoker ctx_call stays advertised so unlisted tools remain reachable — add ctx_call to disabled_tools to make this allowlist authoritative. Example: tools_enabled = ["ctx_read", "ctx_shell", "ctx_search"]

§persona: Option<String>

Active context persona (persona-spec-v1). Selects the domain bundle — tool surface, read-mode/compressor/chunker defaults, intent taxonomy, sensitivity floor. Override via LEAN_CTX_PERSONA. Defaults to coding.

§loop_detection: LoopDetectionConfig§rules_scope: Option<String>

Controls where lean-ctx installs agent rule files. Values: “both” (default), “global” (home-dir only), “project” (repo-local only). Override via LEAN_CTX_RULES_SCOPE env var.

§rules_injection: Option<String>

Controls how rules are injected for shared-instruction-file agents. Values: “shared” (default, marker block in CLAUDE.md/CODEBUDDY.md/AGENTS.md/GEMINI.md), “dedicated” (never touch those files; use each agent’s config-driven auto-load: SessionStart hook / instructions[] / context.fileName, #343), or “off” (write no rules file at all — for hosts that supply their own tool-steering workflow or phase-isolated/non-caching harnesses, #361). Override via LEAN_CTX_RULES_INJECTION env var.

§permission_inheritance: Option<String>

Mirror the host IDE’s tool-permission rules onto lean-ctx’s own MCP tools. Values: “off” (default) or “on”. When “on”, lean-ctx reads the active IDE’s permission config (v1: OpenCode) and applies the equivalent deny/ask/allow decision to the matching lean-ctx tool — so ctx_shell honors your bash/rm * rules instead of bypassing them. Override via LEAN_CTX_PERMISSION_INHERITANCE env var.

§extra_ignore_patterns: Vec<String>

Extra glob patterns to ignore in graph/overview/preload (repo-local). Example: ["externals/**", "target/**", "temp/**"]

§terse_agent: TerseAgent

Controls agent output verbosity via instructions injection. Values: “off” (default), “lite”, “full”, “ultra”. Override via LEAN_CTX_TERSE_AGENT env var.

§compression_level: CompressionLevel

Unified compression level (replaces separate terse_agent + output_density). Values: “off” (default), “lite”, “standard”, “max”. Override via LEAN_CTX_COMPRESSION env var.

§compression_aggressiveness: Option<f64>

Global compression intensity 0.0 (lossless) – 1.0 (max), mapped onto the read modes / entropy / IB stages (see core::aggressiveness). None (default) keeps each mode’s built-in default. Override via the LEAN_CTX_AGGRESSIVENESS env var or the ctx_read aggressiveness arg.

§archive: ArchiveConfig

Archive configuration for zero-loss compression.

§memory: MemoryPolicy

Memory policy (knowledge/episodic/procedural/lifecycle budgets & thresholds).

§allow_paths: Vec<String>

Additional paths allowed by PathJail (absolute). Useful for multi-project workspaces where the jail root is a parent directory. Override via LEAN_CTX_ALLOW_PATH env var (path-list separator).

§allow_ide_config_dirs: Option<bool>

Allow jailed tool access to home-level IDE config dirs (~/.cursor, VS Code, Cline/Roo, JetBrains, …). Tri-state: None = not asked yet (setup prompts once), Some(false) = declined, Some(true) = opted in. Those dirs can expose other agents’ sessions, MCP configs and credentials, so the effective default is off. ~/.lean-ctx (own data dir) is always allowed. The opt-in set is registry-derived, covering every supported editor. Override via LEAN_CTX_ALLOW_IDE_DIRS=1.

§extra_roots: Vec<String>

Extra project roots for multi-root workspaces. Tools like ctx_tree and ctx_search can scan across all roots in a single call. These paths are automatically added to PathJail’s allow-list. Override via LEAN_CTX_EXTRA_ROOTS env var (path-list separator).

§read_only_roots: Vec<String>

Read-only roots: sibling subtrees the agent may READ but never WRITE. Reads resolve as if they were extra_roots; every write tool (edit, refactor, handoff/session export, memory compaction) is default-denied inside these paths. Useful for reference repos mounted next to the project. Override via LEAN_CTX_READ_ONLY_ROOTS env var (path-list separator).

§allow_symlink_roots: Vec<String>

Extra trusted roots OUTSIDE $HOME that lean-ctx may follow when an agent config file/dir (~/.claude.json, ~/.codex/config.toml, …) is a symlink pointing there (#596). Empty by default → the strict $HOME-only boundary stays in force (a planted symlink can never redirect a config write out of the user’s home, preserving the GL#442 symlink-hijack protection). Add a parent like /opt/dotfiles only for a location you own and trust. Like extra_roots, security-sensitive: stripped from untrusted project-local configs. Override via LEAN_CTX_ALLOW_SYMLINK_ROOTS env var (path-list sep).

§content_defined_chunking: bool

Enable content-defined chunking (Rabin-Karp) for cache-optimal output ordering. Stable chunks are emitted first to maximize prompt cache hits.

§minimal_overhead: bool

Skip session/knowledge/gotcha blocks in MCP instructions to minimize token overhead. Override via LEAN_CTX_MINIMAL env var.

Default true (deliberate): initialize-time instructions stay byte-stable across sessions, which keeps the provider prompt-cache prefix warm (#498) and holds the fixed per-session cost at the doctor overhead --gate budget. Session continuity is NOT lost — the wakeup briefing (task, findings, knowledge) is delivered through the first tool call’s --- AUTO CONTEXT --- block instead, which only bills when the agent actually works. Set to false to additionally inject the ACTIVE SESSION / PROJECT MEMORY blocks directly into the MCP initialize instructions.

§symbol_map_auto: bool

Opt-in: substitute long identifiers with short α-codes (+ a §MAP table) in aggressive reads for projects with >50 source files. Off by default — the abbreviated form is confusing for editing/refactoring, where the agent needs the real package and symbol names. Enable for max exploration savings.

§structure_first: bool

Opt-in: bias auto toward structure-first reads (map) for medium code files on a cold read. Off by default — interactive sessions keep the conservative full floor that avoids a follow-up body read. Enable for phase-isolated harnesses (no warm-session cache payback), where a cold full read is pure overhead and structure-first reads aid localization. Override via the LEAN_CTX_STRUCTURE_FIRST env var.

§auto_mode_learning: bool

Opt-in: let the adaptive learning signals (predictor, bandit, heatmap, adaptive policy, bounce/path memory) participate in auto mode resolution. Off by default (#683): the default cascade is a deterministic function of (file, task) — only capability guards and the size/task heuristic decide — which keeps output byte-stable for provider prompt caching (#498) and avoids per-read disk I/O from the learning stores. Override via the LEAN_CTX_AUTO_MODE_LEARNING env var.

§team_url: Option<String>

Team server URL for opt-in savings roll-up. Set via lean-ctx config set team_url https://... or [team] url in config.toml. Override via LEAN_CTX_TEAM_URL env var.

§team_token: Option<String>

Bearer token for the team server (Authorization header on savings push / pull). Set via lean-ctx config set team_token <tok> or team_token in config.toml. Override via the LEAN_CTX_TEAM_TOKEN env var.

§team_auto_push: bool

Opt-in: when true, the running daemon periodically pushes this machine’s signed savings batch to team_url so the team roll-up fills itself (no manual savings push per dev). Off by default; requires team_url + team_token. Set via lean-ctx config set team_auto_push true.

§journal_enabled: bool

Enable human-readable activity journal (~/.lean-ctx/journal.md).

§auto_capture: bool

Opt-in: auto-persist interesting findings as knowledge facts.

§search: HybridConfig

Hybrid search weights (BM25/dense/candidates).

§graph: GraphConfig

Code-graph settings, including traversal (co-access) edges (#289).

§index: IndexConfig

Index-time file filters (#735): include/exclude globs + gitignore handling, applied by every index builder via core::index_filter.

§skillify: SkillifyConfig

Skillify miner settings (#290): codify recurring patterns into rules.

§summaries: SummariesConfig

AI session-summary settings (#292): periodic, semantically-recallable summaries.

§llm: LlmConfig

Optional LLM enhancement (query expansion, contradiction explanation).

§embedding: EmbeddingConfig

Semantic-embedding engine settings (which local ONNX model to use).

§shell_hook_disabled: bool

Disable shell hook injection (the _lc() function that wraps CLI commands). Override via LEAN_CTX_NO_HOOK env var.

§shadow_mode: bool

Shadow mode (default: true): denies native tools (Read/Grep/Shell) at the permission level, forcing agents to use ctx_* MCP tools for maximum compression. Without this, many harnesses silently prefer native tools, negating lean-ctx’s token savings. Disable with shadow_mode = false.

§hook_mode: Option<String>

Global hook mode override. When set, overrides the per-agent auto-detection.

  • replace: Native Read/Grep/Glob/Shell denied, lean-ctx MCP is the only path
  • hybrid: MCP + shell hooks for compression (legacy)
  • mcp: MCP server only, no hooks

Default: unset (auto-detect per agent via recommend_hook_mode)

§debug_log: bool

Opt-in (#520): write a human-readable debug log of intercepted MCP tool calls and hook routing decisions (lean-ctx vs native, with reasons) to <state_dir>/logs/debug.log. Override via the LEAN_CTX_DEBUG_LOG env var.

§shell_activation: ShellActivation

Controls when the shell hook auto-activates aliases.

  • agents-only: (Default since #699) Aliases only active when an AI agent env var is detected — transparent in plain human terminals.
  • always: Aliases active in every interactive shell (pre-#699 default).
  • off: Aliases never auto-activate (user must call lean-ctx-on manually).

Override via LEAN_CTX_SHELL_ACTIVATION env var.

§skip_agent_aliases: bool

Do not install agent CLI aliases (claude, codex, gemini, codebuddy) into ~/.zshrc / ~/.bashrc during onboard / setup. Existing alias blocks are removed when this is toggled on (#754). Does NOT affect the shell compression hook (_lc()) — use shell_hook_disabled for that. Orthogonal to shell_activation which controls when aliases activate, not whether they are installed.

§read_redirect: ReadRedirect

Controls the native-Read → ctx_read redirect hook (#637).

  • auto: (Default) redirect everywhere except hosts with a native read-before-write guard (Claude Code / CodeBuddy), where the path-swap would break native Write/Edit.
  • on: always redirect (legacy behavior).
  • off: never redirect native Read.

Override via the LEAN_CTX_READ_REDIRECT env var.

§read_dedup: ReadDedup

Controls the PostToolUse native-Read re-read dedup (GL #1140).

  • auto: (Default) replace only re-reads of unchanged files, and only on guard hosts (Claude Code / CodeBuddy) where the PreToolUse redirect is disabled — the guard-safe way to win the dedup savings back.
  • on: dedup wherever the PostToolUse hook fires.
  • off: never replace a Read result.

Override via the LEAN_CTX_READ_DEDUP env var.

§update_check_disabled: bool

Disable the daily version check against leanctx.com/version.txt. Override via LEAN_CTX_NO_UPDATE_CHECK env var.

§updates: UpdatesConfig§context: ContextConfig

Fixed-context budget accounting for doctor overhead / gain (#964).

§bm25_max_cache_mb: u64

Maximum BM25 cache file size in MB. Indexes exceeding this are quarantined on load and refused on save. Override via LEAN_CTX_BM25_MAX_CACHE_MB env var.

§graph_index_max_files: u64

Maximum number of files scanned by the lightweight JSON graph index. 0 = unlimited (default). Set >0 to cap for constrained systems.

§memory_profile: MemoryProfile

Controls RAM vs feature trade-off. Values: “low”, “balanced” (default), “performance”. Override via LEAN_CTX_MEMORY_PROFILE env var.

§memory_cleanup: MemoryCleanup

Controls how aggressively memory is freed when idle. Values: “shared” (default, 1h TTL), “aggressive” (5 min TTL for low-memory devices). Override via LEAN_CTX_MEMORY_CLEANUP env var.

§max_ram_percent: u8

Maximum percentage of system RAM that lean-ctx may use (default: 5). Override via LEAN_CTX_MAX_RAM_PERCENT env var.

§max_disk_mb: u64

Simplified disk budget (MB). When set and detail values are at defaults, distributes proportionally: archive=25%, bm25=10%, remainder for stores. 0 = disabled (use individual settings). Override via LEAN_CTX_MAX_DISK_MB.

§max_staleness_days: u32

Auto-purge data older than this many days. 0 = disabled. Flows into archive.max_age_hours and lifecycle idle TTL.

§max_index_threads: usize

Cap on the rayon worker threads used by the CPU-heavy index build (call graph etc.). 0 = rayon default (all cores). Set >0 to bound per-instance CPU so a fleet of concurrent sessions can’t saturate the host on startup. Override via LEANCTX_INDEX_THREADS env var.

§savings_footer: SavingsFooter

Controls visibility of token savings footers in tool output. Values: “always” (default, show on every response), “never”, “auto” (legacy compatibility). Override via LEAN_CTX_SAVINGS_FOOTER or LEAN_CTX_SHOW_SAVINGS=1|0 env var.

§project_root: Option<String>

Explicit project root override. When set, lean-ctx uses this instead of auto-detection. This prevents accidental home-directory scans when running from $HOME. Override via LEAN_CTX_PROJECT_ROOT env var.

§lsp: HashMap<String, String>

LSP server overrides. Map language name to custom binary path. Example: [lsp]\nrust = "/opt/rust-analyzer"\npython = "~/.venvs/main/bin/pylsp"

§ide_paths: HashMap<String, Vec<String>>

Per-IDE allowed paths. Restricts which directories lean-ctx will scan/index for each IDE. Example: [ide_paths]\ncursor = ["/home/user/projects/app1"]\ncodex = ["/home/user/codex"] When set, only these paths are indexed for the matching agent. Global allow_paths still applies.

§model_context_windows: HashMap<String, usize>

Custom model context window overrides. Example: [model_context_windows]\n"my-custom-model" = 500000

§response_verbosity: ResponseVerbosity

Controls how much detail tool responses include.

  • full (default): complete compressed output
  • headers_only: metadata line only (path, mode, token count)

Override via LEAN_CTX_RESPONSE_VERBOSITY env var.

§bypass_hints: Option<String>

Bypass hint mode. When agents use native Read/Grep instead of lean-ctx tools, a hint is appended to the next tool response. Values: “on” (default), “off”, “aggressive” (hint on every call, no cooldown). Override via LEAN_CTX_BYPASS_HINTS env var.

§cache_policy: Option<String>

Cache policy for ctx_read. Controls behavior on cache hits. Values: “aggressive” (default, 13-tok stubs + compaction-aware reset), “safe” (delivers map instead of stub), “off” (no caching, always disk read). Override via LEAN_CTX_CACHE_POLICY env var.

§cache_max_tokens: usize

Token budget for the in-memory ctx_read cache. When the cached total plus an incoming read would exceed this, lean-ctx evicts the least-valuable entries immediately (RRF: recency × frequency × size) so the read always proceeds — eviction is never deferred to the staleness TTL. 0 uses the built-in default (2M). LEAN_CTX_CACHE_MAX_TOKENS env var overrides this.

§boundary_policy: BoundaryPolicy

Cross-project boundary policy. Controls whether cross-project search/import is allowed and whether access is audited.

§secret_detection: SecretDetectionConfig§sensitivity: SensitivityConfig

Per-item sensitivity model with a uniform policy floor (#212). Disabled by default → fully no-op until sensitivity.enabled = true.

§gateway: GatewayConfig

MCP Tool-Catalog Gateway (#210): aggregate + query-route downstream MCP servers. Global-only (never merged from project-local config) and a full no-op until gateway.enabled = true.

§gateway_server: GatewayServerConfig

Self-hosted org gateway server ([gateway_server], enterprise#20): deployment parameters for the usage cockpit — seat count for the org-wide projection, display label, and the central admin API the local cockpit may read from. All optional; absent = local-only behavior.

§addons: AddonsConfig

Addon ecosystem security floor (#863): install policy, registry-signature requirement and sandboxing for spawned addon servers. Global-only (never merged from project-local config) and fully permissive by default.

§allow_auto_reroot: bool

Allow automatic project-root re-rooting when absolute paths outside the jail are seen. When false (default), absolute paths outside the jail are rejected without re-rooting. Override via LEAN_CTX_ALLOW_REROOT env var.

§hook_binary: Option<String>

Verbatim binary path/expression for generated agent-hook commands (#708). Users who sync agent settings (~/.claude/settings.json, …) across machines with different usernames need an env-based form like $HOME/.local/bin/lean-ctx — agent hosts run hook commands through a shell, which expands it. When set (env LEAN_CTX_HOOK_BINARY wins, then this key), every hook writer emits the value verbatim instead of the machine-absolute exe path, so init/doctor --fix/update stop rewriting synced files into sync ping-pong. Autostart plists/services and daemon spawns are NOT affected — launchd/systemd do not expand shell variables, so those keep the real absolute path. Empty (default) = automatic absolute-path resolution (#367).

§path_jail: Option<bool>

Disable PathJail entirely by setting path_jail = false in config.toml. Useful in container/Docker environments where the sandbox is the boundary. (The former LEAN_CTX_NO_JAIL=1 env override was removed in v3.7.3.)

§sandbox_level: u8

Sandbox level for code execution (ctx_exec). 0 = subprocess only (current), 1 = OS-level restriction (Seatbelt/Landlock). Override via LEAN_CTX_SANDBOX_LEVEL env var.

§reference_results: bool

When true, large tool outputs (>4000 chars) are stored as references and a short URI is returned instead of the full content. Override via LEAN_CTX_REFERENCE_RESULTS env var.

§agent_token_budget: usize

Default per-agent token budget. 0 means unlimited. Override per-agent via ctx_session or programmatically.

§shell_allowlist: Vec<String>

Optional shell command allowlist. When non-empty, only commands whose base binary is in this list are permitted by ctx_shell. Empty = disable allowlist (allow all). Default includes common dev tools. Set to [] to disable. Override via LEAN_CTX_SHELL_ALLOWLIST env var (comma-separated).

§shell_allowlist_extra: Vec<String>

Extra commands MERGED on top of the effective shell_allowlist without replacing the defaults. Setting shell_allowlist replaces the whole built-in list (a common footgun); entries here are purely additive, which is what lean-ctx allow <cmd> writes. Only applied in restricted mode (when the base allowlist is non-empty).

§shell_strict_mode: bool

When true, block command substitution ($(), backticks) and process substitution (<(), >()) in shell arguments. When false (default), only warn via tracing. Default false preserves backward compatibility — set true for maximum security.

§shell_security: Option<String>

Shell-security mode for ctx_shell / lean-ctx -c command gating (GL #788): enforce (default, secure), warn (run checks, log violations, never block) or off (skip the allowlist + dangerous-pattern blocks entirely — a deliberate opt-out; compression stays active). Override via LEAN_CTX_SHELL_SECURITY. None resolves to enforce.

§shell_timeout_secs: Option<u64>

Default shell-command timeout in seconds for normal commands. None resolves to the built-in 2-minute default; heavy builds/tests use Config::shell_heavy_timeout_secs. Override via LEAN_CTX_SHELL_TIMEOUT_SECS (LEAN_CTX_SHELL_TIMEOUT_MS still wins over both, in milliseconds).

§shell_heavy_timeout_secs: Option<u64>

Shell-command timeout in seconds for heavy commands (cargo build/test, make, docker build, git commit/push, …). None resolves to the built-in 10-minute ceiling. Override via LEAN_CTX_SHELL_HEAVY_TIMEOUT_SECS.

§shell_allow_writes: bool

When true, ctx_shell accepts shell file-write redirects (>, >>, tee, heredoc-to-file, curl -o, wget default mode). Default false — the native Write/Edit tool is preferred. Opt-in for power users who want classic shell syntax; the real command gating (allowlist, dangerous-pattern and interpreter-eval blocks) still applies. Override via LEAN_CTX_SHELL_ALLOW_WRITES=1.

§shell_allow_inline_scripts: bool

#814: opt-in to allow python3 -c, node -e, etc. in ctx_shell. Default false — inline code is blocked because it leaves no auditable artifact. Override via LEAN_CTX_SHELL_ALLOW_INLINE_SCRIPTS=1.

§setup: SetupConfig

Setup behavior: controls what gets injected during setup and updates.

Implementations§

Source§

impl Config

Source

pub fn provenance() -> ConfigProvenance

Snapshot the provenance of the editable settings (GH #450).

Reads the same sources as Config::load (honoring the #356 TCC guard for the project-local file) plus the live environment, so the result matches what a fresh load() would resolve. Pure: it never mutates the config cache or writes to disk.

Source§

impl Config

Source

pub fn crush_verbatim_json_enabled(&self) -> bool

Whether opt-in lossless JSON crushing of verbatim data commands (#936) is active. LEAN_CTX_CRUSH_VERBATIM_JSON (any value) wins, then the crush_verbatim_json config flag, else false.

Source

pub fn resolved_proxy_bind_host(&self) -> IpAddr

Effective proxy bind address (gateway mode, enterprise#8). Precedence: LEAN_CTX_PROXY_BIND_HOST env > proxy_bind_host config > loopback. The value must parse as an IP address; anything else (including a blank) resolves to 127.0.0.1 — a typo can only ever narrow exposure, never silently open the listener.

Source

pub fn rules_scope_effective(&self) -> RulesScope

Returns the effective rules scope, preferring env var over config file.

Source

pub fn rules_injection_effective(&self) -> RulesInjection

Returns the effective rules injection mode, preferring env var over config. Default is Shared (zero-config discovery via a CLAUDE.md/CODEBUDDY.md/AGENTS.md block).

Source

pub fn hook_mode_override(&self) -> Option<HookMode>

Returns the user-configured hook mode override, or None for auto-detect. Env var LEAN_CTX_HOOK_MODE takes priority over config.

Source

pub fn permission_inheritance_effective(&self) -> PermissionInheritance

Returns the effective permission-inheritance mode, preferring the LEAN_CTX_PERMISSION_INHERITANCE env var over config. Default is Off. Accepts on/true/1 as enabled.

Source

pub fn dedicated_session_context_active(&self) -> bool

True when lean-ctx should inject its rules via each agent’s dedicated, non-polluting auto-load path and global rules are in scope.

Gates the Claude/Codex SessionStart additionalContext summary: it stands in for the (now-skipped) shared CLAUDE.md/CODEBUDDY.md/AGENTS.md block, so it only fires when injection is Dedicated and the scope isn’t project-only.

Source

pub fn disabled_tools_effective(&self) -> Vec<String>

Returns the effective disabled tools list, preferring env var over config file. When prefer_native_editor is active, the lean-ctx edit tools are folded in so they are hidden from list_tools (#454).

Source

pub fn prefer_native_editor_effective(&self) -> bool

Whether lean-ctx edit operations are disabled in favour of the host’s native editor (#454). LEAN_CTX_PREFER_NATIVE_EDITOR wins over config.

Source

pub fn max_index_threads_effective(&self) -> usize

Cap on the rayon index-build worker threads. LEANCTX_INDEX_THREADS wins over config; 0 means “no cap” — rayon’s all-cores default is kept.

Source

pub fn edit_tool_blocked(&self, name: &str) -> bool

Whether name is a lean-ctx edit operation that must be blocked from dispatch (direct and via ctx_call) when Self::prefer_native_editor_effective is set (#454). Read/search/shell/memory tools are never blocked.

Source

pub fn minimal_overhead_effective(&self) -> bool

Returns true if minimal overhead is enabled via env var or config.

Source

pub fn structure_first_effective(&self) -> bool

Returns true if structure-first auto reads are enabled.

The LEAN_CTX_STRUCTURE_FIRST env var wins over the config field, and accepts the usual truthy/falsy spellings so a harness can flip it per run (LEAN_CTX_STRUCTURE_FIRST=0 forces it off even if config enables it).

Source

pub fn auto_mode_learning_effective(&self) -> bool

Returns true when the adaptive learning signals may participate in auto mode resolution (#683). Off by default for a deterministic, I/O-light cascade; the LEAN_CTX_AUTO_MODE_LEARNING env var wins over the config field and accepts the usual truthy/falsy spellings.

Source

pub fn is_stochastic_enabled(&self) -> bool

Returns true when probabilistic exploration (Thompson sampling, Boltzmann-temperature eviction, simulated annealing) may influence decisions. Off by default so tool output stays a deterministic, byte- stable function of (content, mode, task) — the determinism contract (#498) that lets provider prompt caching apply. The LEAN_CTX_STOCHASTIC env var wins (the usual truthy/falsy spellings); otherwise it follows Self::auto_mode_learning_effective, which is itself off by default.

Source

pub fn minimal_overhead_effective_for_client(&self, client_name: &str) -> bool

Returns true if minimal overhead should be enabled for this MCP client.

This is a superset of minimal_overhead_effective():

  • LEAN_CTX_OVERHEAD_MODE=minimal forces minimal overhead
  • LEAN_CTX_OVERHEAD_MODE=full disables client/model heuristics (still honors LEAN_CTX_MINIMAL / config)
  • In auto mode (default), certain low-context clients/models are treated as minimal to prevent large metadata blocks from destabilizing smaller context windows (e.g. Hermes + MiniMax).
Source

pub fn shell_hook_disabled_effective(&self) -> bool

Returns true if shell hook injection is disabled via env var or config.

Source

pub fn shell_activation_effective(&self) -> ShellActivation

Returns the effective shell activation mode (env var > config > default).

Source

pub fn shell_allow_writes_effective(&self) -> bool

Returns true if ctx_shell may accept shell file-write redirects. LEAN_CTX_SHELL_ALLOW_WRITES (1/true/yes/on) overrides config.toml. The real command gating still applies either way.

Source

pub fn shell_allow_inline_scripts_effective(&self) -> bool

#814: returns true if ctx_shell may accept inline interpreter scripts (python3 -c "...", node -e "...", etc.). LEAN_CTX_SHELL_ALLOW_INLINE_SCRIPTS (1/true/yes/on) overrides config.toml. The real command gating (allowlist) still applies.

Source

pub fn update_check_disabled_effective(&self) -> bool

Returns true if the daily update check is disabled via env var or config.

Source

pub fn memory_policy_effective(&self) -> Result<MemoryPolicy, String>

Source

pub fn default_tool_categories_effective(&self) -> Vec<String>

Returns the effective set of default tool categories. Priority: LCTX_DEFAULT_CATEGORIES env var > config.toml > hardcoded default.

Source

pub fn tool_profile_effective(&self) -> ToolProfile

Returns the effective tool profile. Priority: LEAN_CTX_TOOL_PROFILE env > config tool_profile > config tools_enabled > active persona’s tool surface > power.

Explicit settings win (backward compatible); when none are set, the active persona supplies the tool surface (the coding default resolves to power, so existing installs are unaffected).

Source

pub fn sensitivity_effective(&self) -> SensitivityConfig

The [sensitivity] config with the active persona’s floor folded in (persona-spec-v1). Enforcement chokepoints use this instead of the raw field so a persona like lead-gen (sensitivity_floor = "confidential") protects PII out of the box. The coding default (public) passes the config through unchanged.

Source

pub fn no_degrade_effective(&self) -> bool

Returns true if all automatic read-mode degradation is disabled. Checks LCTX_NO_DEGRADE env var first, then config.toml field.

Source

pub fn delta_explicit_effective(&self) -> bool

Returns true if explicit full/lines:N-M re-reads of cached-but-changed files should be served as deltas (mode=diff) instead of re-emitting full content.

Checks the LCTX_DELTA_EXPLICIT env var first, then the config.toml field. Unlike a presence-only knob, an explicit 0/false in the env forces the feature OFF even when the config field is true, so the env can fully override config in both directions.

Source

pub fn max_disk_mb_effective(&self) -> u64

Effective max_disk_mb from env or config.

Source

pub fn max_staleness_days_effective(&self) -> u32

Effective max_staleness_days from env or config.

Source

pub fn context_budget_tokens_effective(&self) -> usize

Effective fixed-context budget (tokens) from env or config (#964). 0 (env or config) disables the warning; otherwise the per-session footprint is checked against this in doctor overhead and gain.

Source

pub fn archive_max_disk_mb_effective(&self) -> u64

Archive max_disk_mb derived from simplified max_disk_mb if the detail value is still at its default. Explicit overrides take priority.

Source

pub fn archive_max_age_hours_effective(&self) -> u64

Archive max_age_hours derived from max_staleness_days if the detail value is still at its default. Explicit overrides take priority.

Source

pub fn bm25_max_cache_mb_effective(&self) -> u64

Effective on-disk ceiling (MB) for the persisted BM25 index. Single source of truth for save/load, cache prune, and the doctor health check.

Priority: explicit bm25_max_cache_mbmax_disk_mb budget (10%) › generous default (DEFAULT_BM25_PERSIST_MB). The default is decoupled from the RAM profile so large repos persist instead of rebuilding forever (issue #249).

Source§

impl Config

Source

pub fn path() -> Option<PathBuf>

Returns the path to the global config file ($XDG_CONFIG_HOME/lean-ctx/config.toml).

Resolves via crate::core::paths::config_dir so config lives in the RO-safe config category. Behavior-neutral today: config_dir() equals the legacy data dir for existing/single-dir installs (GH #408 / GL #602).

Source

pub fn missing_config_path() -> Option<PathBuf>

Some(path) when the global config the runtime resolves does not exist, so lean-ctx is silently on built-in defaults. None when a config file is present (or HOME is unresolvable).

The directory is layout-dependent (XDG ~/.config/lean-ctx vs legacy ~/.lean-ctx vs $LEAN_CTX_DATA_DIR) and an MCP client may launch the server in a sandbox/container with a different $HOME. An edit made to a different config.toml than this one is silently ignored; the block messages use this to say so out loud over MCP, where the stderr path is invisible (#540).

Source

pub fn local_path(project_root: &str) -> PathBuf

Returns the path to the project-local config override file.

Source

pub fn load() -> Self

Loads config from disk with caching, merging global + project-local overrides.

The cache is keyed on a content hash of the global + project-local files, not their mtime. mtime-only invalidation silently served a stale Config whenever a content edit preserved the mtime (coarse filesystem mtime resolution, cp -p, atomic save-then-rename, two edits within the same second). A long-lived MCP server then kept the old value (e.g. path_jail) while a fresh lean-ctx doctor process — with an empty cache — saw the new one (#406). Config files are tiny, so reading + hashing them on every load is negligible and guarantees liveness.

Source

pub fn load_arc() -> Arc<Self>

Shared-ownership variant of load: returns the cached Arc<Config> so the per-dispatch hot path bumps a refcount instead of deep-cloning the whole struct. Liveness is identical to load — the global and project-local files are still read and content-hashed on every call (#406); only the cache payload became an Arc, so a cache hit is a cheap Arc::clone.

Source

pub fn load_global() -> Self

Loads ONLY the global config file — never merging project-local .lean-ctx.toml overrides, and bypassing the in-memory cache. Every PERSIST path must use this (or Config::update_global): Config::load folds per-project overrides into the struct, and Config::save writes the whole struct back to the GLOBAL file — so a load → mutate → save round-trip silently leaks per-project values (and, historically, reset customized keys) into the global config (#443). Reading global-only makes the save leak-free by construction.

Source

pub fn update_global<F>(f: F) -> Result<Self, LeanCtxError>
where F: FnOnce(&mut Self),

Safely mutate and persist the GLOBAL config. Reads the global file only (no project-local merge), applies f, then writes minimally. Refuses (returns Err) when the file exists but is unparseable, so a typo can never clobber a customized config (#443). Returns the saved Config.

This is the canonical persistence entry point: prefer it over Config::load() followed by save(), which leaks project-local overrides into the global file.

Source

pub fn save(&self) -> Result<(), LeanCtxError>

Persists the current config to the global config file.

Preserves user comments, formatting, and unknown keys, keeps the file minimal (defaults that were never set on disk stay implicit), and writes atomically with a .bak backup so customizations are always recoverable.

Source

pub fn show(&self) -> String

Formats the current config as a human-readable string with file paths.

Trait Implementations§

Source§

impl Clone for Config

Source§

fn clone(&self) -> Config

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Config

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Config

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for Config
where Config: Default,

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for Config

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more