Configuration reference
Environment variables and on-disk paths for sf-intelligence. Most installs need none of these - the defaults are safe for offline vault use. New here? Start with the install guide.
Live read-only plane
The live plane is off by default. Vault tools never call Salesforce. Enable it any one of three ways:
| Method | Scope | How |
|---|---|---|
| Standing consent | Per org, persists | sfi.live_consent { grant: true, orgAlias: "my-org" } |
| Environment | All live tools in the MCP process | SFI_LIVE_PLANE_ENABLED=1 |
| Per-call flag | A single tool invocation | pass liveEnabled: true in the tool args |
Revoke standing consent with sfi.live_consent { grant: false }. Consent is stored vault-independently (default ~/.sf-intelligence/live-consent.json; override with SFI_CONSENT_PATH) so it works before sfi init.
The curated live-tool roster
There is no generic SOQL tool. When enabled, only these read-only tools run, and they label answers provenance: live_org:
| Tool | What it does |
|---|---|
live_count | SELECT COUNT() for one object |
live_sample | Sample rows (max 200) |
live_field_population | Null vs populated ratio for one field |
live_group_count | Value distribution (GROUP BY one field) |
live_stale_records | Records not touched in N days |
live_recent_activity | Recently created or modified records |
live_aggregate | MIN/MAX/AVG/SUM on one numeric field |
live_duplicate_check | Duplicate values on one field |
live_owner_breakdown | Record counts by owner (User/Queue names) |
live_storage_by_object | Top N objects by record count |
live_describe | Live object describe |
live_org_limits | Governor limit snapshot |
live_inactive_users | Users inactive for N days |
live_drift_check | Compare vault claims vs live org |
live_report_usage | Stale/unused reports (LastRunDate) |
live_folder_access | Folder access types |
live_email_template_usage | Email template usage / migration candidates |
live_license_usage | License allocation vs actual usage |
live_org_health | Failed jobs, paused flows, limits at risk |
live_stale_check | Is the org ahead of the vault? (Tooling-API drift) |
live_picklist_usage | Which picklist values records actually use |
live_budget | Session live-query budget + cache state |
live_consent | Grant/revoke standing live-plane consent (not a query) |
Hybrid answers (vault + live)
Some tools fuse the offline vault (structure - what depends on what) with a live read-only query (magnitude - how many records), stamping provenance: hybrid and carrying both planes' freshness. When the org is ahead of the vault, a hybrid answer leads with a staleness warning rather than narrating a fresh count against stale structure. Live data never backfills stale vault claims.
| Tool | Hybrid value |
|---|---|
blast_radius_live | The static impact graph + a live affected-record count per record-bearing dependency |
what_if_make_field_required | Pass liveEnabled: true to add the field's live production null-rate |
field_change_advisor | Pass liveEnabled: true to cite the live record population alongside the vault verdict |
live_picklist_usage | Defined value set (vault) × actual usage (live) |
Without consent these tools still answer from the vault, with a caveat - the static answer is never blocked on the live plane.
Live-plane cost controls
| Variable | Default | Purpose |
|---|---|---|
SFI_LIVE_QUERY_BUDGET | 50 | Max live org queries per server session; the plane fails closed with an actionable message when spent. A cache hit costs nothing. |
SFI_LIVE_CACHE_TTL_MS | 90000 | TTL for the in-process live-result cache. A repeated identical query inside the session is served from cache, stamped cached: true with the original read time. Memory-only - never persisted. |
SFI_BLAST_RADIUS_MAX_LIVE | 25 | Per-call cap on live COUNT queries for blast_radius_live; beyond it the answer is marked partial. |
Governance & observability
| Variable | Default | Purpose |
|---|---|---|
SF_INTELLIGENCE_AUDIT_LOG | (unset - no logging) | Append-only JSONL path. Logs tool name, argument keys only (never values), vault hash, timestamp. Best-effort; never breaks tool calls. |
export SF_INTELLIGENCE_AUDIT_LOG="$HOME/.sf-intelligence/audit.jsonl"
Multi-vault / fleet
Cross-vault tools (fleet_find, compare_vaults, fleet_drift_ranking, …) need a registry of vault roots.
| Variable | Default | Purpose |
|---|---|---|
SF_INTELLIGENCE_REGISTRY_PATH | walk up for registry.json, else ~/sf-intelligence-vaults | Path to registry.json or a directory containing it |
Register vaults with the CLI (the file is created on first use), then list them:
sfi register-vault acme-prod /path/to/acme-prod/org-kb sfi register-vault acme-sandbox /path/to/acme-sandbox/org-kb sfi list-vaults
Vault paths must be absolute. Per-vault freshness is read from each vault's own meta/manifest.json, never duplicated in the registry - so a vault refreshed independently never goes stale in the registry. fleet_drift_ranking runs the staleness probe across every registered vault and ranks them by how far each is behind its live org, so a team running many orgs knows which to refresh first (it's a live sweep, so per-org consent and the session budget both apply).
Concurrent read-only serving (the vault lock)
The MCP server opens the vault read-only, taking a shared DuckDB lock - so several read-only consumers can serve the same vault at once (an IDE's sfi mcp server, a CI/eval harness, a fleet dashboard) with no conflict. Only a sfi refresh needs the single exclusive writer lock; if a refresh runs while a server is serving, it fails fast with an actionable error naming the likely holder and the remedy. The rule is many readers, one writer.
HTTP serving (sfi serve --http)
Read-only MCP over streamable HTTP for CI pipelines or shared localhost access. Requires a bearer token (constant-time compare; --generate-token prints one once). Binds 127.0.0.1 by default - non-loopback --host warns and requires a token. The live plane is hard-disabled over HTTP regardless of consent or environment: a remote caller can never reach your org or spend its API budget. Each request gets a fresh read-only context, so a refresh underneath is visible immediately. stdio serving remains sfi mcp.
Staged refresh (sfi refresh --staged)
On large orgs, get a servable vault in minutes instead of waiting for the full pull:
- T0 (seconds) - read-only COUNT queries write a skeleton org card (
partial: true) and a manifest whose coverage rows are allpending. - T1 (minutes) - retrieves the 10 priority families behind most questions (objects, fields, validation rules, Flows, Apex, layouts, record types, Profiles, PermissionSets). The vault answers questions about those types from here on.
- T2 - full monolithic refresh through a transactional side-build: the live graph is replaced only on success, so an interruption leaves the T1 vault servable.
- T3 - only with
--with-reports: the folder-based Report/Dashboard pass.
Mid-build honesty is automatic: sfi.health_check reports degraded (building tier i/n), absence answers stay qualified until the type's tier has landed, and re-running --staged skips completed tiers.
Usage-ranked Reports & Dashboards (default pull)
Folder-based Reports/Dashboards are invisible to the wildcard retrieve, so every full refresh pulls the top SFI_REPORTS_CAP (default 500) ranked by actual usage (LastRunDate / LastViewedDate) via read-only SOQL. Field references fold onto fields - no separate report nodes. When the org holds more than the cap, coverage rows read pending and absence claims stay qualified. --with-reports = uncapped full pull; --no-reports = skip.
AST-parsed Apex edges (default on)
During refresh, a parser-grade Apex pass builds parsed-confidence dependency edges from real AST analysis - cross-method references, SOQL in loops, and call graphs that regex heuristics miss. Opt out with --no-apex-ast at the cost of weaker Apex edge confidence. Parsed edges are deduplicated and aggregated at the class/method level.
Vault git history (sfi vault git enable)
Optional: turn org-kb/ into its own git repo (generated .gitignore keeps graph DB, snapshots, and caches out). After enabling, every refresh whose source tree changed auto-commits with a per-type delta message; unchanged refreshes commit nothing; git failure never fails a refresh. Consumers: sfi.component_history (timeline + capped diff) and sfi.component_as_of (properties at a ref). Non-git vaults answer available: false with the enable hint.
Annotations overlay (meta/annotations.jsonl)
Curated meaning the org cannot carry - owner, status (e.g. deprecated), glossary synonyms, domain, note - event-sourced and surviving every refresh. Humans write via sfi annotate; AI proposes via sfi.propose_annotation (always confirmed: false). Consumers serve annotations with provenance annotation - never offline_snapshot. Orphans (annotated IDs whose component left the graph) surface in the refresh pulse and sfi annotate orphans.
Question-gap log (local telemetry)
When routing cannot answer a question, the router may append a gap record locally - never sent to the network - so the library grows toward what people actually ask.
| Variable / path | Default | Purpose |
|---|---|---|
SFI_GAP_LOG_PATH | (unset) | Override the gap-log path (tests) |
| default file | ~/.sf-intelligence/question-gaps.jsonl | Local gap log for product review |
MCP server registration
The full register-once steps live in the install guide. The MCP server resolves the vault as org-kb/ relative to the current working directory (your Salesforce DX repo), not the install path.
Note: a handful of additional variables exist for development, evaluation, and release gates (scale budgets, SAST thresholds, eval reporters). Those aren't used in normal sessions.
Most of this is optional.
The defaults are safe and offline. Reach for these only when you want live data or multiple orgs.