Skip to content

Configuration reference

Every setting is declared in one file, server/settings/catalog.py, and is an environment variable LOOM_<FIELD> read at startup into server.settings.settings. Set them in .env or the container environment; .env.example lists the common ones. A secret's default shows as (unset) or (set), never its value: set a secret in the environment, .env or a secrets manager, never in the settings file. LOOM_ENV_FILE names a different dotenv file, and empty reads none.

The same settings, named in lowercase, can go in a YAML settings file, read when LOOM_SETTINGS_FILE names it.

# yaml-language-server: $schema=schemas/loom-settings.schema.json
version: 1
settings:
  memory_search_default_mode: rrf

A value comes from the first of: the environment, the dotenv file, the settings file, the default. A secret (keep those in the environment or a secrets manager), a key given twice, a version this build does not read or a value of the wrong type or outside its bounds (also one the environment or the dotenv file overrides) stops the server at startup; a name no setting has is logged and ignored, since builds N-1 and N share the file during a rolling upgrade. schemas/loom-settings.schema.json is the file's JSON Schema (python scripts/codegen/settings_schema.py), and loom.example.yaml the template with every setting at its default.

A setting marked live also takes a deployment-wide override while Loom runs, which wins over all of these: loom settings set <field> <value> or POST /v1/admin/settings.set. Every process applies it within LOOM_RUNTIME_OVERRIDES_REFRESH_SECONDS, and it outlives a restart until loom settings reset <field>. loom settings list shows each setting's value in force and where it came from (runtime settings).

This page is generated from the comments above each field in server/settings/ by python scripts/codegen/settings_reference.py; edit the comment, not this file.

Default changes

Every change to a setting's default, from server/settings/_history.py. A deployment that never set the variable changes with each one.

Release Variable Was Is Why
1.2.0 LOOM_KNOWLEDGE_INGEST_INTERVAL_SECONDS 2 0 The ingest worker takes the next job as soon as one ends instead of waiting 2 s after every job, so a queue of small documents drains at the speed of its jobs; a pass that finds no job still waits LOOM_KNOWLEDGE_INGEST_IDLE_INTERVAL_SECONDS.
1.2.0 LOOM_CLICKSTACK_USER_ID_KEYS 'user_id,user.id,enduser.id' 'user_id,user.id,enduser.id,loom.user_id,langfuse.user.id' A user-scoped ClickStack erasure also matches the user-id keys Loom's own child spans carry, so they are erased with the request span. A deployment that sets the variable keeps its own list and should add both keys.
1.0.0 LOOM_EXTRACTION_QUEUE_MAX_ATTEMPTS 3 8 A failed replay now waits 15 s, 1, 5, then 30 minutes before the next attempt, and an unreachable provider spends none: eight attempts span about two hours instead of three back to back.

Assistant

The assistant: LibreChat run headless beside Loom, and the agent Loom seeds in it.

Variable Type Default Live Meaning
LOOM_ASSISTANT_LIBRECHAT_URL str '' LibreChat's internal address (http://librechat:3080), reachable only from Loom. "" = no assistant: no service setup at boot, and the panel's chat is off; its starters still read.
LOOM_ASSISTANT_SERVICE_EMAIL str 'loom-assistant@loom.invalid' Email of the LibreChat service user that owns the agent, read once, when the first boot writes loom.assistant_service; LibreChat sends it no mail.
LOOM_ASSISTANT_SETUP_RETRY_S float 60.0 Seconds before a failed assistant setup is tried again; each next failure doubles the wait, up to 16 times as long (docs/architecture/assistant.md#deployment).
LOOM_ASSISTANT_ANSWER_TTL_S int 600 Seconds one assistant answer may run, and how long its delegated token lives.
LOOM_ASSISTANT_BUDGET_SHARE float 0.5 The share of a namespace's daily model cost cap the assistant may spend, so questions cannot spend what extraction and the conflict resolver need.
LOOM_ASSISTANT_CONCURRENT_ANSWERS int 2 Answers one web process runs at once per namespace (a replica runs its web workers' count times this); bounds how far answers that each passed the cap check can overshoot it.
LOOM_ASSISTANT_ANSWER_CEILING_USD float 0.1 USD charged for an answer cut before LibreChat reported its usage (a disconnect, a timeout) or on a model with no price, so no answer is free.

Auth

Authentication, tokens and identity providers.

Variable Type Default Live Meaning
LOOM_PLATFORM_ADMINS str '' Comma-separated login handles and emails promoted to platform_admin (org-wide visibility). A handle matches a password account's subject; an email matches only an IdP-verified (OIDC) email, never the unverified self-signup address. Empty = no platform admins.
LOOM_AUTH_REQUIRED bool True True: every operation requires a valid bearer JWT. False (laptop demos only): missing tokens are not rejected and the SPA synthesizes a local identity. identity_provider=single-user forces auth off regardless.
LOOM_DEFAULT_NAMESPACE str 'local' The one namespace auth-off mode serves (SPA identity, dashboard bootstrap, MCP/SDK caller defaults). Ignored when auth is required; personal-* is reserved and rejected.
LOOM_OAUTH_ENABLED bool False MCP one-click connect: dynamic client registration and /oauth/authorize on top of the sign-in routes (device grant, token, revoke) every auth-on deployment serves. Opt-in: anyone can register a client.
LOOM_OAUTH_ISSUER str '' Public base URL used in OAuth discovery and redirects; empty derives it from each request. Required when oauth_login_provider is "oidc" (the IdP callback URL is built from it).
LOOM_OAUTH_LOGIN_PROVIDER 'password' \| 'oidc' 'password' How people sign in (dashboard, loom login, MCP connect): "password" (Loom's own form) or "oidc" (the external IdP; loom maps the verified email to an account and still mints its own token). "oidc" closes password sign-in and self-signup, and requires oauth_issuer and the three oidc_* values.
LOOM_OAUTH_ACCESS_TOKEN_TTL_SECONDS int 3600 Lifetime of an OAuth access token, in seconds; at most a day, which bounds access after the IdP deactivates someone (only a refresh asks it).
LOOM_PASSWORD_LINK_TTL_SECONDS int 86400 Seconds a password set-up link stays valid, at most a week (it sets a password once).
LOOM_OAUTH_SIGNIN_STATE_SWEEP_INTERVAL_SECONDS int 300 Seconds between passes of the always-on sweep that drops sign-in state (loom.oauth_signin_state keys) past its credential's expiry.
LOOM_OIDC_ISSUER str '' External IdP issuer URL; its /.well-known/openid-configuration must name the same issuer.
LOOM_OIDC_CLIENT_ID str '' Client id registered at the IdP; also the audience each id_token must carry.
LOOM_OIDC_CLIENT_SECRET str (unset) Client secret sent to the IdP token endpoint when exchanging the sign-in code.
LOOM_OIDC_BACKCHANNEL_BASE_URL str '' Origin used for server-side sign-in calls to the issuer, preserving its Host header. Empty uses the issuer directly; browser redirects and bot-trust key fetches are unchanged.
LOOM_OIDC_JIT_PROVISION bool False JIT provisioning: a first OIDC sign-in with no account creates a personal namespace in oidc_jit_org instead of being rejected. Off = fail closed. oidc_jit_email_domains (comma-separated) restricts it; empty admits any verified email the IdP authenticates.
LOOM_OIDC_JIT_ORG str '' Org that JIT-provisioned accounts are created in; required when oidc_jit_provision is on.
LOOM_OIDC_JIT_EMAIL_DOMAINS str '' Comma-separated email domains JIT provisioning admits; empty admits any verified email.
LOOM_IDENTITY_PROVIDER_TIMEOUT_S float 10.0 live Seconds one call to an identity provider may take: OIDC discovery, the token and revocation endpoints, its signing keys, a machine issuer's discovery, and an end-user issuer's JWKS fetch.
LOOM_SCIM_TOKEN str (unset) Bearer tokens (comma-separated, any one accepted) an identity provider presents to deactivate and reactivate people over SCIM 2.0 (/scim/v2). Empty = off; needs auth on, oidc.
LOOM_JWT_SECRET str (set) The root secret, and the one secret a deployment carries: it signs tokens as kid default and seals every stored credential and every signing key in loom.signing_keys (loom signing-key rotate adds those). Required when auth is on. The default is the checked-in DEV secret anyone can mint with: a deployment with auth on refuses to start with it, except the make dev stack (loom doctor warns everywhere else). A non-loopback bind refuses an HMAC secret under 32 bytes or an RSA key under 2048 bits.
LOOM_JWT_SECRET_PREVIOUS str (unset) The root secret LOOM_JWT_SECRET replaced, while a rotation is under way: credentials and signing keys sealed under it still open (tokens it signed as kid default do not verify: a leaked root mints nothing). loom secrets reseal rewrites the stored ones under the new root; then remove this. "" = none.
LOOM_JWT_KEYRING_REFRESH_SECONDS float 30.0 Seconds between re-reads of loom.signing_keys in every process (at least 1); loom signing-key rotate waits out two of the slowest process's.
LOOM_CELL str '' This cell's name (1 to 32 of a-z, 0-9, '-') behind a router, "" = not a cell; set, the process serves only while its cell's rows accept it (docs/architecture/cells.md).
LOOM_CELL_PROBE_PORT int 0 The port of the probe listener, which answers the deployment id and the values under loom cell prepare's nonce keys (docs/architecture/cells.md); 0 = off.
LOOM_CELL_ROUTER_URL str '' The router's base URL a cell calls to place a new org, and the loom cell commands' default router; "" = none: in enforced, a new org answers 503.
LOOM_CELL_ROUTER_TOKEN str (unset) The credential a cell calls the router with: lcr_ then openssl rand -hex 64 (never the length of a SHA-256); any other form is refused at boot. "" = none.
LOOM_CELL_ROUTER_TIMEOUT_S float 5.0 Seconds a whole placement call to the router may take before the request answers 503.
LOOM_TENANCY_MODE_CACHE_S float 5.0 Seconds (at least 1) between reads of the cell state rows once the first read landed (every second until then), in every process that reads them under the Postgres authority, and before an org read as not held is read again; LOOM_JWT_REVOCATION_MAX_STALE_SECONDS must be at least three times this.
LOOM_ROUTER_PG_DSN str (unset) The router's directory, its own Postgres database (loom router serve and loom router migrate); "" = none. Never a cell's memory authority database.
LOOM_ROUTER_PLACEMENTS_PER_MINUTE int 60 Placement calls one cell may make in a minute, counted by each router replica.
LOOM_ROUTER_DIRECTORY_CACHE_S float 5.0 Seconds (at least 1) a router replica keeps the cells' credential hashes and a placed org's lookup answer before reading again; a new credential or host works within this.
LOOM_ROUTER_LOOKUPS_PER_MINUTE int 60 Org lookups one client address may make in a minute, counted by each router replica (GET /cells/lookup).
LOOM_ROUTER_SHARED_HOST str '' The deployment's shared host (loom.example.com) the router serves by rules 2 to 4, beside the first cell's own host and aliases; "" = no cell registers (registration tells the first cell by this host), and once one has, only its own host and aliases.
LOOM_ROUTER_UPSTREAM_URL str '' Where the router forwards the shared host until a first cell registers, which copies it into its row; "" = 503 there and no first cell registers.
LOOM_ROUTER_UPSTREAM_PROBE_URL str '' The probe listener address of the deployment behind the shared host, at which the first cell's registration is checked; "" = no first cell can be registered.
LOOM_ROUTER_OPERATOR_SHA256 str '' SHA-256 (64 lowercase hex) of the router's operator credential, which loom cell limit, loom cell credential and loom cell prepare call its operator API with; "" = that API refuses.
LOOM_ROUTER_OPERATOR_TOKEN str (unset) The operator credential itself, for the CLI that calls the router's operator API: lco_ then openssl rand -hex 64 (loom cell credential new --operator). "" = none.
LOOM_JWT_ISSUER str 'loom' The iss Loom stamps on every token it mints; a verified token carries it or one of jwt_trusted_issuers.
LOOM_JWT_AUDIENCE str 'loom-platform' The aud claim stamped on minted tokens and required on every verified token.
LOOM_JWT_TRUSTED_ISSUERS str '' Extra accepted iss values, for a gateway minting Loom-shaped JWTs with a key in jwt_public_keys_json. Loom's own tokens (iss = jwt_issuer) pass.
LOOM_JWT_PUBLIC_KEYS_JSON str (unset) JSON (or base64 JSON) map kid → PEM public key of a gateway whose RS256 tokens this deployment accepts (a non-loopback bind refuses an RSA key under 2048 bits). Loom's own keys live in loom.signing_keys.
LOOM_JWT_LEEWAY_SECONDS int 30 Clock-skew tolerance on iat/exp, seconds; small enough to give replay no useful post-exp window.
LOOM_JWT_REVOCATION_CACHE_TTL_SECONDS float 30.0 Seconds the loom.revoked_tokens snapshot is cached; verify checks both jti and sub against it, so a revocation takes effect on other processes within this TTL (at once on the one that wrote it).
LOOM_JWT_REVOCATION_MAX_STALE_SECONDS float 600.0 Oldest revocation snapshot a process still trusts, seconds. Past it (refreshes keep failing) every bearer is refused with a retryable 503. A subject's cached grants are trusted as long when a re-read fails.
LOOM_GRANT_CACHE_TTL_SECONDS float 10.0 Seconds a subject's grant rows are cached for the namespace gate, which caps a token's claim at the grant it holds there. The backstop: a grant change reaches other processes through the per-org grant watermark (acl_watermark_poll_seconds), and this bounds one whose watermark row was never written. It also bounds how long a SCIM deactivation of an email, or a bot disable, takes to stop a trusted caller acting for it on another process, and how long a bot created there takes to be acted for.
LOOM_ACL_WATERMARK_POLL_SECONDS float 1.0 Seconds between each process's reads of the per-org grant watermark (loom.acl_versions): a revoke on one replica is enforced on every other within about this long.
LOOM_ACL_WATERMARK_MAX_STALE_SECONDS float 10.0 Oldest grant watermark a process still trusts, seconds. Past it (polls keep failing) cached grants are not served and every request reads its own.
LOOM_AUTH_ATTEMPTS_CONCURRENCY int 8 Sign-ins, sign-ups and workload-token exchanges one process counts at once (each costs ClickHouse statements, a sign-in a password hash); past it a caller waits up to a second, then gets 429 (an exchange 503), so a burst cannot take the shared connection pool.
LOOM_WORKLOAD_EXCHANGES_PER_TRUST int 50000 Workload-token exchanges (RFC 8693) one trust may make in 10 minutes, deployment-wide; past it /oauth/token answers 429 until older ones age out.
LOOM_WORKLOAD_EXCHANGES_PER_AGENT int 300 Workload-token exchanges one agent of a trust (a token with an agent claim) may make in 10 minutes, so one agent's loop cannot spend the trust's. A trust without agents has only workload_exchanges_per_trust.
LOOM_SELF_SIGNUP 'off' \| 'open' 'off' Browser self-signup: "off" (endpoint 404s) or "open" (/v1/auth/self-signup mints JWTs scoped to the requested namespace). A non-loopback bind refuses "open" unless identity_provider is "trust-network", allow_insecure is set, or oauth_login_provider is "oidc" (which closes sign-up).
LOOM_SELF_SIGNUP_ORG str 'dev' Default org for self-signed tokens.
LOOM_IDENTITY_PROVIDER '' \| 'single-user' \| 'trust-network' \| 'gateway' '' How callers authenticate: "single-user" (no auth, one anonymous identity), "trust-network" (self-signup, first claim wins) or "gateway" (an upstream proxy mints short-lived Loom JWTs with the shared HS256 secret; Loom only validates). Empty = inferred from auth_required and self_signup at boot.
LOOM_DASHBOARD_REDIRECT_URL str '' When set, a dashboard page load redirects (302) here instead of serving the SPA (Loom behind an aggregator). Deployment-specific; never hardcode.
LOOM_TRUSTED_CALLERS str '' Comma-separated subjects allowed to send X-Loom-On-Behalf-Of (a trusted aggregator passing each end-user's identity under one service token). Empty disables the header.
LOOM_JWT_END_USER_ISSUERS_JSON str '' The services that sign a short-lived token per end user (a chat app in front of Loom): JSON (or base64 JSON) {issuer: {"orgs": [...], "jwks_url": "https://…", "keys": {kid: RS256 public PEM}}}. orgs lists the orgs its tokens may name (exact names, or prefix*); its keys come from jwks_url (fetched every jwt_end_user_jwks_refresh_seconds) and/or keys. A key here verifies only its own issuer's tokens, and only as an end user (sub: user:<id>, one listed org, an optional agent: agent:<id>), never any other token. Empty: no end-user tokens. A non-loopback bind refuses an inline RSA key under 2048 bits (a key fetched from jwks_url is not checked).
LOOM_JWT_END_USER_MAX_TTL_SECONDS int 300 The longest an end-user token may live, from its iat to its exp.
LOOM_JWT_END_USER_JWKS_REFRESH_SECONDS float 300.0 Seconds between fetches of each end-user issuer's jwks_url; a key it rotates in is honoured from the next fetch.
LOOM_RESTRICTED_SERVE bool True Kill switch for restricted namespaces. True: served to a principal whose token names the namespace AND who holds a live direct grant. False: every read is refused while ingest continues.
LOOM_RESTRICTED_AUDIT_INLINE bool True Wait for the namespace.restricted_access audit row of each admitted restricted read before answering; false writes it beside the answer, lost if the process dies first.
LOOM_CORS_ORIGINS str '' Comma-separated origin list. "*" allows all (acceptable when auth_required=True). Empty disables CORS entirely.
LOOM_MAX_REQUEST_BYTES int 33554432 Request-body cap, bytes, enforced by ASGI middleware before any parser (413 PAYLOAD_TOO_LARGE); uvicorn has no limit of its own. 0 disables.
LOOM_HSTS_MAX_AGE_SECONDS int 31536000 Strict-Transport-Security max-age, seconds; browsers ignore it over plain HTTP. 0 sends no header.
LOOM_POLICY_MODE 'off' \| 'shadow' \| 'enforce' 'off' History policy over the agent trace: "off" reads/writes nothing; "shadow" records verdicts in loom.policy_decisions without blocking; "enforce" denies (403 POLICY_FORBIDDEN) and records them too. In enforce, writes fail closed on evaluation errors; reads never do.
LOOM_POLICY_MAX_WINDOW_SECONDS int 86400 Cap on any rule's look-back window, in seconds.
LOOM_POLICY_CACHE_TTL_SECONDS int 30 Per-(org, namespace) rule cache TTL, seconds; other replicas see a rule change within it.

Timeouts, budgets and caches

Variable Type Default Live Meaning
LOOM_AUTH_MACHINE_DISCOVERY_TTL_S int 3600 live Seconds a machine issuer's discovery document (its JWKS URL) is cached.
LOOM_AUTH_OIDC_DISCOVERY_TTL_S float 3600.0 live Seconds the identity provider's OpenID configuration is cached.

ClickHouse

ClickHouse connection, query shaping and vector storage.

Variable Type Default Live Meaning
LOOM_CH_HOST str 'localhost' Host name of the ClickHouse server Loom stores everything in.
LOOM_CH_PORT int 8123 ClickHouse HTTP port; 8443 (the Cloud HTTPS port) turns TLS on when ch_secure is unset.
LOOM_CH_USER str 'loom' User name the server connects to ClickHouse as.
LOOM_CH_PASSWORD str (set) Password for ch_user; the default only suits a local development server.
LOOM_CH_DATABASE str 'loom' The database Loom's schema lives in: every table, view and the migrations ledger, and the connection's default database. Statements are written against loom and name this database where they reach ClickHouse (server/core/ch_names.py); KeeperMap paths move with it. Set it before the first migration: while a schema is in loom, a server pointed at a name that does not exist, or holds tables but no Loom ledger, refuses to start (create it empty to start a second schema). A letter or _, then letters, digits, _ and -; never default, system, information_schema, or a table or alias name the schema uses. Read once at startup.
LOOM_CH_TELEMETRY_DATABASE str 'loom_telemetry' The database the telemetry Loom derives from its own tables lives in (sli_minute), outside the schema so a reader can be granted it alone. A schema outside loom names its own: two schemas never share one.
LOOM_CH_CLUSTER str 'default' The cluster clusterAllReplicas reads to find a stopped Loom process's queries on every node (default on ClickHouse Cloud). A server without it answers for itself; '' reads only the connected server.
LOOM_CH_SECURE bool \| None None Connect over HTTPS. Unset infers TLS from the port (8443 on, anything else off).
LOOM_CH_LOG_COMMENT_STATEMENT_DIGEST bool False Append sql:<shape12> <first 200 chars> of each statement to its log_comment (" "), so query_log rows group by shape. A statement that carries a secret, or one run as the analyst, gets its shape alone. Off by default: readers that split log_comment on spaces gain tokens.
LOOM_CH_CAPTURE_STATEMENTS str '' A file path: append one JSON line (digest, sql) per executed statement, to collect the shapes an acceptance run executes for the analyzer corpus. '' = off; never set it in production.
LOOM_CH_POOL_MAXSIZE int 64 HTTP connection pool size: request concurrency plus 3 x the batched-insert bound (inserts a second x wait / 1000; docs/architecture/clickhouse.md#batched-inserts).
LOOM_CH_KEEPALIVE_TIMEOUT float 5.0 Seconds an idle pooled connection is kept. Must stay BELOW the Cloud load balancer's idle cutoff (8 s) so loom closes first: a peer-closed pooled TLS connection is never reaped under uvloop (LOOM_UVICORN_LOOP=uvloop).
LOOM_CH_READ_YOUR_WRITES bool True A point lookup that MISSES right after a write (get after set, verify, update_by_id, delete) re-reads once with select_sequential_consistency=1. Costs only on a miss; a no-op on single-node MergeTree.
LOOM_CH_READ_YOUR_WRITES_WINDOW_S float 20.0 memory.search on a namespace this process wrote within this many seconds reads with select_sequential_consistency from the start. 0 = off (the empty-pool retry remains the backstop).
LOOM_CH_TIMEOUT_READ_S float 30.0 Deadline, seconds, for read statements, applied as max_execution_time and as a client-side bound that frees the pool slot. 0 = no bound for reads.
LOOM_CH_TIMEOUT_WRITE_S float 60.0 Deadline, seconds, for INSERT and UPDATE statements; 0 = no bound.
LOOM_CH_TIMEOUT_DDL_S float 300.0 Deadline, seconds, for DDL statements (CREATE, ALTER, DROP); 0 = no bound.
LOOM_CH_BATCHED_INSERTS bool True live Batch the inserts written on every request into shared parts; false commits them, and the waited async inserts keep their bounds (docs/architecture/clickhouse.md#batched-inserts).
LOOM_CH_BATCHED_INSERT_WAIT_MIN_MS int 10 live Shortest wait, ms, of a batched or waited async insert: what a lone insert waits when writes are sparse; capped at ch_batched_insert_wait_max_ms (or the background bound).
LOOM_CH_BATCHED_INSERT_WAIT_MAX_MS int 50 live Longest wait, ms, of a batched or waited async insert a request waits on; it holds a pooled connection meanwhile.
LOOM_CH_BATCHED_INSERT_BACKGROUND_WAIT_MAX_MS int 200 live Longest wait, ms, of the usage-event flusher's ticks and shutdown drain and the write-mark timer's flush, which no request waits on.
LOOM_CH_POOL_WAIT_MS int 500 Max wait, ms, for a pool slot before failing fast with 503 OVERLOADED. 0 = no slot gate.
LOOM_CH_REPLICA_BOUND_RETRIES int 2 Fresh connections a statement tries after a replica-bound error (a replica that lost its Keeper session, a replica's read-only table). Behind a load balancer one reconnect can land on the replica that just failed, so a single retry is not enough.

Caller-shaped read ceilings

Ceilings for the reads a caller shapes (code.grep, code.search with a pattern or globs, memory.aggregate, memory.record.query). A ceiling only lowers the statement's own value, so no request can widen it; 0 = none. The shipped loom_reader profile (nlp_user_settings.xml) caps these three at their defaults, with a floor no statement can go under (time: 0.05 s, code.grep's statement floor), so none can be set to 0 even in the SQL. Raising a setting above its cap needs the profile raised too; a 0 sends no ceiling, so the profile's cap applies (code.grep's deadline is then clamped to ch_timeout_read_s, which the profile's cap matches).

Variable Type Default Live Meaning
LOOM_CH_CALLER_SHAPED_MAX_EXECUTION_S float 30.0 Seconds one caller-shaped read may run (the read class default); 0 = no ceiling, so the loom_reader profile's cap applies.
LOOM_CH_CALLER_SHAPED_MAX_MEMORY_BYTES int 4000000000 Bytes of memory one caller-shaped read may use; 0 = no ceiling, so the loom_reader profile's cap applies.
LOOM_CH_CALLER_SHAPED_MAX_ROWS_TO_READ int 500000000 Rows one caller-shaped read may scan; past it the read fails (422), never a partial answer. 0 = no ceiling, so the loom_reader profile's cap applies.
LOOM_CH_READER_USER str '' ClickHouse user the caller-shaped reads run as, with ch_password; the shipped config defines loom_reader (SELECT only). Empty = ch_user.

Analysis statements (insights.query)

Caps and pacing for insights.query. The statement caps are pinned as a MAX in the loom_analyst user's caps profile so no statement can raise them; a boot whose ClickHouse user has access management writes that profile when one changed.

Variable Type Default Live Meaning
LOOM_CH_ANALYSIS_MAX_EXECUTION_S float 10.0 Seconds one insights.query statement may run.
LOOM_CH_ANALYSIS_MAX_ROWS_TO_READ int 200000000 Rows one statement may scan; past it the statement fails (422), never a partial answer.
LOOM_CH_ANALYSIS_MAX_MEMORY_BYTES int 2000000000 Bytes of memory one statement may use.
LOOM_CH_ANALYSIS_MAX_RESULT_ROWS int 500 Rows one statement returns at most; a longer result comes back cut, marked truncated.
LOOM_CH_ANALYSIS_MAX_RESULT_BYTES int 4000000 Bytes of result one statement may return; past it the statement fails (422).
LOOM_CH_ANALYSIS_MAX_CONCURRENT int 8 Statements the analyst user runs at once across every org and replica.
LOOM_CH_ANALYSIS_MAX_THREADS int 4 Threads one statement may use (pinned as a MAX, so a statement cannot take the host).
LOOM_CH_ANALYSIS_MAX_CONCURRENT_PER_ORG int 2 Statements one org runs at once on one replica; past it a call answers RATE_LIMITED.
LOOM_CH_ANALYSIS_PROBE_INTERVAL_S float 60.0 live Seconds between two probes of the analyst user in each web process; a problem an admin fixes, or a repair a write makes, shows within one interval.

Traffic classes

Variable Type Default Live Meaning
LOOM_CH_SETTINGS_BY_TRAFFIC_CLASS str '{"production": {"priority": 1}, "interactive": {"priorit… JSON map {traffic class: ClickHouse settings} applied by server.core.ch.statement_settings to every read (an explicit per-call setting wins). priority: 1 served first, larger waits, 0 opts out; max_threads caps CPU per statement. Neither changes the rows returned. background (workers, operator CLIs) bypasses the filesystem cache on a miss so a backfill scan does not evict interactive data.

Migrations

Variable Type Default Live Meaning
LOOM_BOOT_MIGRATE bool True Apply pending migrations at boot. Set False for any server started from a branch against a shared service, or it applies unmerged migrations.
LOOM_BOOT_MIGRATE_LEASE_TTL_S int 300 Seconds after which the boot migration lease's holder, once it stops renewing, is replaced.
LOOM_BOOT_MIGRATE_LEASE_WAIT_S float 900.0 Seconds a contender waits for the holder before boot fails (exit 1, the container restarts); also how long a booting process waits for the provider configuration lock (a sibling's rebuild step may hold it).
LOOM_MIGRATE_MUTATION_WAIT_S int 600 Seconds a migration file waits for the mutations it started (MATERIALIZE, UPDATE, a column type change) to finish on every replica; past it the file fails unrecorded.
Variable Type Default Live Meaning
LOOM_VECTOR_SEARCH_QUANTIZED_CODES bool True Send vector_search_use_quantized_codes = 1 on vector-ranked reads whose column has a Quantized(...) codec and whose table has no patch parts: a codes-first scan plus full-precision rescore. A replica that rejects the setting (code 115) drops the fragment and retries.
LOOM_VECTOR_SEARCH_QUANTIZED_FETCH_MULTIPLIER float 3.0 Over-fetch for the codes-first read: stage 1 keeps LIMIT × this by code distance, stage 2 rescores. Only read when the codes setting is sent.
LOOM_VECTOR_CODES_STATE_PROBE_SECONDS int 60 Seconds between re-reads of which vector columns are coded and which coded tables carry patch parts. A write that embeds reads again on its own when the reading is older than a few seconds (codes_state.FRESH_READING_S).

Timeouts, budgets and caches

Variable Type Default Live Meaning
LOOM_SCHEMA_STATUS_TTL_S float 60.0 live Seconds the schema status (applied against shipped migrations) is cached for the health probe.

Code

The code plane: ingest, indexes, search, ask, review.

Switch, test gate and code.ask

Variable Type Default Live Meaning
LOOM_CODE_INTEL_ENABLED bool True Master kill switch, checked at the top of every code.* handler but the retirement (code.repo.remove), which only deletes.
LOOM_CODE_TEST_GATE_RUN_TEMPLATES str '' code.test_gate run recipes (LOOM_CODE_TEST_GATE_RUN_TEMPLATES): JSON list of {"pattern": , "run":