Skip to main content

Environment variables

Every knob is optional — chat-recall runs with sane defaults. This is the single reference for the CHAT_RECALL_* variables, plus the server's auth and billing ones (grep the source for the authoritative behavior; defaults below are current at time of writing).

Collector / sync (the chat-recall CLI + chat-recall-watch daemon)​

VariableDefaultWhat it does
CHAT_RECALL_UPLOAD_TIMEOUT_MS90000Per-request abort timeout for conversation uploads.
CHAT_RECALL_FULL_BUILD_MAX_MB64Transcripts larger than this aren't fully materialized on a FULL sync — only the newest tail ships (prevents OOM on huge sessions).
CHAT_RECALL_TAIL_APPENDonSet 0 to disable incremental tail-append and force FULL syncs (emergency off-switch for the offset-continuity path).
CHAT_RECALL_EXTERNAL_SCANNERSoffSet 1 to enable the external secret detectors (gitleaks/trufflehog). Off by default: they are third-party subprocesses that need pre-redaction text materialized on disk, and coverage would vary per device. Built-in regex redaction + findings and tenant rules run for everyone regardless.
CHAT_RECALL_SCAN_SECRETSonSet 0 to skip the external secret scanners even when they're enabled above (e.g. to keep a huge backfill fast).
CHAT_RECALL_BATCHSCAN_MAX_MB64Cap on pre-redaction text the external-detector batch scan keeps on disk at once. The batch dir is scanned and deleted each time it crosses this, bounding what a SIGKILL/OOM can strand in /tmp.
CHAT_RECALL_INCLUDE_FUZZYoffSet 1 to keep low-precision detector findings (generic-api-key, URI, …) instead of dropping them.
CHAT_RECALL_SYNC_TRACEoffSet 1 to print per-phase progress + heap/RSS to stderr — how walk OOMs get localized.
CHAT_RECALL_REDACT_INDEX—Index-time redaction toggle (sync-path redaction is always on regardless).
CHAT_RECALL_AUTO_UPDATEon (self-host)Controls the daemon's checksum-pinned same-origin self-update.
CHAT_RECALL_WATCH_WINDOW_DAYS14How recently a transcript must have been written for the daemon to put a file watcher on it. chokidar takes one watch descriptor per matched file, and a finished transcript never changes, so watching every session ever written cost 14,000 descriptors and 900 MB of heap on a machine with 13,929 of them. Sessions older than the window still sync — the 15-minute heartbeat does a full ledger walk — they just are not detected the instant they change.
CHAT_RECALL_OIDC_ISSUER—Fallback OIDC issuer for chat-recall login SSO when the server doesn't advertise one and --issuer isn't passed.

Auto code-intelligence (collector)​

VariableDefaultWhat it does
CHAT_RECALL_CODE_INDEXonSet 0 to disable automatic codeindex discovery/indexing.
CHAT_RECALL_CODE_INDEX_MINUTES180How often the code-index pass runs.
CHAT_RECALL_CODE_INDEX_MAX50Max workspaces indexed per pass.
CHAT_RECALL_CODE_INDEX_DAYS120Recent-activity window for discovering workspaces.
CHAT_RECALL_WITH_CODEINDEX—Force-download the codeindex companion during init.

Path overrides​

Point the collector at non-standard tool locations (useful for fixtures/tests):

VariableOverrides
CHAT_RECALL_CLAUDE_HOME~/.claude
CHAT_RECALL_CODEX_HOME~/.codex
CHAT_RECALL_AGY_HOMEthe agy tool home
CHAT_RECALL_CURSOR_HOME~/.cursor (the cursor-agent CLI store)
CHAT_RECALL_CURSOR_IDE_HOME~/.config/Cursor (the DESKTOP app — note the capital C; ~/.config/cursor is the CLI's auth dir)
CHAT_RECALL_HERMES_HOME~/.hermes, or %LOCALAPPDATA%\hermes on Windows; HERMES_HOME when it is set. Named profiles in its profiles/ folder are read too.
CHAT_RECALL_PI_HOME~/.pi/agent (Pi's agent dir); PI_CODING_AGENT_DIR when it is set.
CHAT_RECALL_PI_SESSION_DIR<agent dir>/sessions, unless PI_CODING_AGENT_SESSION_DIR or an absolute sessionDir in <agent dir>/settings.json names another folder.
CHAT_RECALL_OPENCODE_DB~/.local/share/opencode/opencode.db (full file path)
CHAT_RECALL_DATA_DIR~/.chat-recall (ledger, shadow archive, credentials)

Server / storage (self-host)​

Set on the server container, not the CLI. See the quick-start header of docker-compose.yml for the canonical set.

VariableWhat it does
CHAT_RECALL_STORAGEpostgres (product) — required; the server fails closed rather than guessing a backend. (sqlite exists for unit tests only, not a user-facing mode.)
CHAT_RECALL_DATABASE_URL / ..._URL_ROPostgres connection string(s) for bring-your-own-Postgres.
OIDC_ISSUERThe server's OIDC issuer — advertised to CLIs via /api/capabilities for SSO login.
ADMIN_KEY, AUTH_PROVIDERAdmin key and auth mode (none for a single-tenant local box).
SECRET_RESCAN0 disables the daily re-scan of ALREADY-STORED (redacted) text with today's rules. That pass is the only thing that can notice a secret a client's redactor missed — it is in the DB in cleartext at that point — so leave it on unless you have a reason.
SECRET_RESCAN_LIMITSessions per re-scan run (default 2000, freshest first).
SECRET_RESCAN_EXCLUDE_TENANTSComma-separated tenants to skip (default synccheck).
RAW_ARCHIVE_S3_BUCKETKeeps the gzipped session archive in an S3-compatible bucket instead of the raw_sessions.gz column. Unset (the default) stores it in Postgres, which is what a self-host with no bucket does. On the hosted database that column was 1232 MB of 3564 MB, all of it TOAST, and only one read path touches it.
RAW_ARCHIVE_S3_ENDPOINTS3 endpoint, e.g. https://s3.example.com. Required with the bucket.
RAW_ARCHIVE_S3_ACCESS_KEY_ID / ..._SECRET_ACCESS_KEYCredentials. Give them an identity scoped to this bucket: object storage has no row-level security, so the only thing keeping one tenant's archive from another is that the key is reachable only through a raw_sessions row, which is behind the tenant and author policies.
RAW_ARCHIVE_S3_REGIONRegion for the signature (default us-east-1).

Existing archives keep working when the bucket is turned on: a row with bytes in gz and no key is read from the column, and one with a key is read from the bucket, so both shapes coexist. Move them with npx tsx scripts/backfill-raw-to-object-store.ts, which is safe to stop and re-run and verifies every object before clearing a row.

Other server-internal flags (CHAT_RECALL_EDITION, _ROLE, _SERVER_MODE, _TENANT, _FEATURE_*, _VECTOR_PARTITIONS, _TELEMETRY) tune SaaS/edition behavior; consult the server source before setting them.

Billing (Stripe)​

Set on the server. Billing is entirely optional: with STRIPE_SECRET_KEY absent the server boots with billing off, and a self-host box never needs any of these. Stripe owns the amounts — the server reads prices back from Stripe, so a price change in the dashboard needs no deploy.

Required to sell anything:

VariableWhat it does
STRIPE_SECRET_KEYSecret API key (sk_live_… / sk_test_…). Its presence is what turns billing on.
STRIPE_WEBHOOK_SECRETEndpoint signing secret (whsec_…). The webhook verifies over the raw bytes; without this it rejects every event.
BILLING_PLANSJSON array of the plans this deployment sells: [{"key":"solo-monthly","label":"Solo","priceId":"price_…","seats":"fixed"}]. seats is fixed (quantity 1) or per_seat (with optional minSeats/maxSeats). A plan with contact and no priceId is contact-only: checkout refuses it and returns the address. Malformed JSON degrades to the legacy single plan below — it never crashes the server.
STRIPE_PRICE_IDThe pre-catalogue single plan. Used only when BILLING_PLANS is unset or unparsable. Keep it for an existing one-plan deployment; prefer BILLING_PLANS for anything new.

Redirects. Set PUBLIC_URL and the three below are derived from it; set one explicitly to send a return somewhere else:

VariableWhat it does
STRIPE_SUCCESS_URLWhere checkout returns after a successful subscribe.
STRIPE_CANCEL_URLWhere checkout returns when the buyer backs out.
STRIPE_PORTAL_RETURN_URLWhere the customer portal returns.

Optional:

VariableDefaultWhat it does
STRIPE_PRODUCT_TAGchat-recallWhich product this deployment sells. Set it if one Stripe account sells more than one product. Stripe fans an event out to every endpoint subscribed to that event type — it does not route by product — so another product's customer.subscription.updated arrives here too. The tag is stamped on every checkout and checked on every event, so buying product B cannot grant a paid entitlement here.
STRIPE_AUTOMATIC_TAXoffSet 1 to let Stripe Tax calculate VAT/sales tax at checkout and collect the buyer's VAT id. Warning: enable this only after Stripe Tax settings report status: active and every live price carries a tax behavior. automatic_tax needs a tax behavior on each line, so turning it on too early fails every checkout on the account — a revenue outage, not a degraded experience. VAT id collection is part of the same switch on purpose: tax calculation without it charges consumer VAT on B2B sales that should have been reverse-charged.
STRIPE_TRIAL_DAYS7Length of the no-card trial.