Configuration reference
Sift reads its settings from environment variables, each named SIFT_ plus the setting’s
name in capitals. There is no configuration file. Every SIFT_* line in .env reaches
every backend container (the API, the three workers and the scheduler), because
docker-compose.yml reads that file as its env_file. A variable you do not set takes
the default below. Settings are read when a container starts: after changing .env, run
make up, which recreates the containers whose settings changed (docker compose restart
does not re-read .env). For how to work with these in practice, and the few most
deployments change, see Configure Sift.
A setting with an invalid value stops the containers at start, with a message naming the setting; the message never prints the value.
Required and runtime
Section titled “Required and runtime”| Variable | Default | Allowed values | Effect |
|---|---|---|---|
SIFT_SECRET_KEY | none: required | Text; in production at least 32 characters and not a key printed in the Sift repository | Key material for signing sessions and encrypting the stored Kindo API key and webhook endpoints’ addresses and secrets. make init generates one (32 random bytes, url-safe base64). Changing it ends every session and makes those stored secrets unreadable. Compose refuses to start without it. |
SIFT_ENVIRONMENT | development | development, production | production refuses to start on any published credential (see below), marks the session cookie Secure (so it needs HTTPS in front), sends webhooks over HTTPS only, and turns off the API’s interactive docs and OpenAPI document (/api/docs, /api/openapi.json). |
SIFT_LOG_LEVEL | INFO | A standard level name, such as DEBUG, INFO, WARNING or ERROR (any case) | Log verbosity of every backend container. |
SIFT_LOG_FORMAT | json | json, console | json writes one JSON object per line; console writes readable lines for a terminal. |
SIFT_METRICS_TOKEN | unset | At least 32 characters | The bearer token GET /api/metrics requires. Unset, there is no metrics endpoint: it answers 404. A shorter token stops the start. Generate one with openssl rand -hex 32. See Monitor Sift. |
SIFT_WORKER_HEARTBEAT | true | true, false | How the system status learns which workers are up. true has each worker report in every 5 seconds, so worker_pool_silent appears up to 15 seconds after a worker stops. false asks the workers directly, which makes every status call, and so every console page, wait a second. |
SIFT_HEALTH_SHARE_S | 2 | Seconds, 0 to 60 | How long the dashboard, Alerts, fleet, Actions and Filters list may show the same reading of each filter’s health, so opening the console works it out once rather than five times. A change to a filter (its state, version or spec) shows at once; a new label or agent run up to this many seconds late. A filter’s own page, approvals and every other decision always read the current state. 0 turns sharing off. |
SIFT_PAGE_CACHE_S | 60 | Seconds, 0 to 600 | The oldest copy of an overview read (the dashboard, Filters table, activity chart, flow, asks, Alerts and the Filters list) the console may be served. Each response says when it was computed, which the console shows as as of. A person’s change (a filter’s state, an approval, a spec save, a label correction, pairing, the Kindo key or model) shows at once; a new score, agent run or label up to this many seconds late. A filter’s own page, approvals and every other decision always read the current state. 0 turns the cache off. |
SIFT_PAGE_REFRESH_S | 30 | Seconds, 1 to 600 | Past this age a cached overview read is still served while it is refreshed in the background, and the console’s default reads are refreshed this often whether or not anyone is looking. At or above SIFT_PAGE_CACHE_S, nothing is served while it refreshes. |
SIFT_SETBASED_READS | true | true, false | How the dashboard, fleet and Activity read their window. true is the faster way, and stays fast right after a restore or a large load. false is the slower way, kept as a fallback. The figures are the same either way. |
In production, Sift refuses to start, naming every problem at once, when:
SIFT_SECRET_KEYis empty, shorter than 32 characters, or a key printed in the Sift repository;SIFT_DEFAULT_ADMIN_PASSWORDisadmin, or shorter than 12 characters;- the Postgres password (
POSTGRES_PASSWORD) is empty orsift; - the object store’s key (
SEAWEEDFS_SECRET_KEY) issiftsiftsift.
make init generates the secret key, POSTGRES_PASSWORD and SEAWEEDFS_SECRET_KEY.
It leaves the admin password at admin, so set that yourself before going to production.
Sign-in
Section titled “Sign-in”| Variable | Default | Allowed values | Effect |
|---|---|---|---|
SIFT_DEFAULT_ADMIN_USERNAME | admin | Text | The account created at first start, with the admin role. |
SIFT_DEFAULT_ADMIN_PASSWORD | admin | Text; in production at least 12 characters and not admin | That account’s first password. Used only to create the account when it does not exist; it never resets an existing one. The console warns while the password is admin. |
SIFT_SESSION_TTL_HOURS | 12 | Whole hours | How long a session lasts when nobody ends it. Signing out, or changing the password, ends every session of that account at once. |
SIFT_TRUSTED_PROXY_HOPS | 1 | 0 to 8 | How many reverse proxies in front of the API append to X-Forwarded-For. The sign-in throttle limits attempts per address, using the address the outermost trusted proxy recorded. 1 is the shipped nginx; 2 behind a load balancer or TLS terminator that also appends; 0 when nothing proxies the API. |
| Variable | Default | Allowed values | Effect |
|---|---|---|---|
SIFT_KINDO_API_BASE_URL | https://api.kindo.ai | URL | The Kindo API host Sift assumes where no connection names one, and the host it recognises as Kindo’s in a webhook address. Sift’s calls go to the address saved with the Kindo connection, entered when an admin connects (https://api.kindo.ai unless changed there). |
SIFT_KINDO_AUTH_HEADER | Authorization | Header name | The header the Kindo API key is sent in. Authorization is sent as Bearer <key>. Sift’s key check tries the other known form (api-key) too, so you rarely need to change this. |
SIFT_KINDO_REQUEST_TIMEOUT_S | 30.0 | Seconds | Time limit for one attempt at a Kindo API call (tool calls, agent and run requests). |
SIFT_KINDO_INFERENCE_TIMEOUT_S | 180.0 | Seconds | Time limit for one attempt at a chat completion (Sift’s assistant, the analyzer, gold-set builds), which takes far longer than an API call. |
SIFT_KINDO_MAX_REQUESTS_PER_SECOND | 5.0 | Requests per second | The most requests Sift sends to Kindo per second. |
SIFT_COPILOT_MODEL | claude-sonnet-5 | A model name in your Kindo tenant | The model for Sift’s own calls (Sift’s assistant, the analyzer, gold-set builds) only while no model is chosen on the Kindo connection page. While it is in use, the system status reports kindo_model_unselected. When the tenant does not list it, Sift uses a model the tenant offers instead. |
SIFT_MAX_TRIGGER_INPUT_CHARS | 32000 | Characters | The most characters one agent run’s input may carry, across all its inputs. Over it the input is cut and the run records that it was (truncated in the agent contract). |
SIFT_MAX_RUN_AGE_S | 21600 (6 hours) | Seconds | An agent run with no verdict after this long is settled as stuck and reported. |
SIFT_DELIVERY_FAILURE_MATURITY_S | 120 | Seconds | How long a failure to deliver an event to the agent must stand, with no successful delivery for the same filter since, before it counts as a delivery failure and stops the filter filtering. Shorter turns a brief Kindo restart into a burst of runs; longer keeps filtering into an outage. |
SIFT_ASSUMED_COST_PER_AGENT_RUN_USD | unset | A number, in US dollars | What one agent run costs you, for the savings figure on the cost views. Unset, Sift reports runs avoided and says the spend is unknown. An empty value is an error, not “unset”: leave the line out instead. |
SIFT_AGENT_DEFAULT_RUNS_PER_HOUR | unset (half of SIFT_KINDO_MAX_REQUESTS_PER_SECOND × 3600: 9000 at the default) | Runs an hour, 1 to 1,000,000 | How many runs one agent can take an hour, for a filter that does not say. The volume check on a new filter compares the stream with it to decide what to suggest. It limits nothing by itself. Unset, it is half of what the Kindo rate limit allows, since Sift’s own calls share that limit. An empty value is an error, not “unset”. |
Gold-set builds
Section titled “Gold-set builds”A build starts whenever the console creates a filter. These bound what one build reads and spends. See The gold set.
| Variable | Default | Allowed values | Effect |
|---|---|---|---|
SIFT_GOLD_POOL_MAX_EVENTS | 500 | Events | How many events a build reads from the source before choosing about 50 from them. |
SIFT_GOLD_RANKED_POOL_MAX_EVENTS | 2000 | 1 to 20000 | The pool a build reads instead when the decision model is on, which then helps choose the proposals from it. |
SIFT_GOLD_PROPOSAL_BATCH_SIZE | 20 | Events | Events per proposal request to the model. |
SIFT_GOLD_MAX_LLM_CALLS | 30 | Calls | The most completions one build may spend. This is the whole inference cost of a build. |
SIFT_GOLD_MAX_EVENT_CHARS | 2000 | Characters | How much of one event a proposal request quotes. |
Whether a filter is worth running, and Collapse
Section titled “Whether a filter is worth running, and Collapse”| Variable | Default | Allowed values | Effect |
|---|---|---|---|
SIFT_SAVINGS_FLOOR | 0.05 | 0 up to, not including, 1 | An operating point that filters less than this share of events is reported as not worth running, and approving it needs an explicit acknowledgement. |
SIFT_SAVINGS_WARN | 0.10 | 0 up to, not including, 1; not below SIFT_SAVINGS_FLOOR | Above the floor but below this share, the point can be approved and is flagged as a thin margin. A value below the floor stops the start. |
SIFT_COLLAPSE_MIN_VERIFIED_CLUSTERS | 30 | 10 or more | How many groups of two or more repeats, with a verdict on the first event and on at least one repeat, a Collapse stage measuring in shadow must have seen before Sift proposes collapsing. The Collect cluster verdicts Setup step counts toward it. |
Model weights and training
Section titled “Model weights and training”Sift’s recipes need a fixed set of model weights, staged into the model-weights volume
on the API’s first start (about 134 MB). The weights are pinned by content digest: a
mismatch stops the container rather than downloading over it. Change the four
SIFT_EMBEDDER_* variables only together, and expect every filter to retrain. See
Install without internet access.
| Variable | Default | Allowed values | Effect |
|---|---|---|---|
SIFT_MODEL_WEIGHTS_DIR | /app/models | Path in the container | Where the weights live. docker-compose.yml sets it to the volume’s mount point. |
SIFT_EMBEDDER_ID | bge-small-en-v1.5 | Text | The weights’ name, and their directory under the weights directory. |
SIFT_EMBEDDER_REPO_ID | BAAI/bge-small-en-v1.5 | Repository id | Where the files are downloaded from. |
SIFT_EMBEDDER_REVISION | 5c38ec7c405ec4b44b94cc5a9bb96e735b38267a | Commit | The pinned revision of that repository. |
SIFT_EMBEDDER_DIGEST | sha256:3befc307e4fac3b03ecb3a54e45e1c2eb0a285d4ccd344a30a316cec7d597603 | sha256: digest | The digest over the pinned file set, recorded on every trained model and checked when one loads. |
SIFT_MODEL_DOWNLOAD_BASE_URL | https://huggingface.co | URL | Where weights are downloaded from. Point it at an internal mirror serving /{repo_id}/resolve/{revision}/{file}. |
SIFT_MODEL_FETCH_ON_START | true | true, false | false only verifies the weights and never opens a connection, so missing files fail at once. Set it on a host without internet access. |
SIFT_MODEL_DOWNLOAD_TIMEOUT_S | 600.0 | Seconds | Time limit for each read during the weight download (connecting has its own 30 seconds). |
SIFT_ML_TRAIN_THREADS | unset | 1 to 256 | CPU threads one training job may use. Unset, it is the host’s cores divided by 2, the number of training jobs the ml worker runs at once. Set it if you change that worker’s concurrency. |
Notifications
Section titled “Notifications”Alerts leave Sift only through webhooks an admin sets up. See Notifications and webhooks.
| Variable | Default | Allowed values | Effect |
|---|---|---|---|
SIFT_CONSOLE_URL | unset | Absolute http:// or https:// address | The console’s address as people reach it, used for the links in alerts. Unset, every link is null. |
SIFT_DEPLOYMENT_NAME | unset | Text | Names this deployment in every alert, for a receiver that hears from several. |
SIFT_WEBHOOK_PRIVATE_NETWORKS | unset (public addresses only) | Comma- or space-separated CIDRs within RFC 1918, fc00::/7 or 100.64.0.0/10 | Private ranges a webhook may reach, for a receiver on your own network. Any other range stops the start. Loopback, link-local and cloud metadata addresses are refused whatever this says. Shown read-only on the Notifications page. |
Decision model
Section titled “Decision model”Off by default; Sift works fully without it. Turning it on sends the fields a filter’s
Decide stage reads, cut to SIFT_DECISIONS_MAX_STATE_CHARS, to TypeSafe’s API. See
Decision model settings.
| Variable | Default | Allowed values | Effect |
|---|---|---|---|
SIFT_JEV_ENABLED | false | true, false | Turns the decision model on. true without SIFT_JEV_API_KEY stops the start. |
SIFT_JEV_API_KEY | unset | Text | The bearer key for TypeSafe’s API. An empty value means unset. |
SIFT_JEV_BASE_URL | https://api.typesafe.ai | Absolute http:// or https:// address | Where decision-model requests go. |
SIFT_JEV_MODEL | jev-1.13.0 | A pinned model version | The model version asked. Every answer records the version that gave it. |
SIFT_DECISIONS_MAX_RPS | 15.0 | Above 0, up to 100 | Requests per second to the decision model, from all of Sift’s processes together. |
SIFT_DECISIONS_MAX_STATE_CHARS | 4000 | 200 to 64000 | The most characters of one event sent to the model. |
SIFT_DECISIONS_TIMEOUT_S | 10.0 | Above 0, up to 120 | Time limit for one decision-model request, in seconds. |
SIFT_VERDICT_CHECK_DAILY_CAP | 200 | 1 to 100000 | The most verdict-check questions per filter per UTC day. New verdicts past it are passed over, not queued for the next day. |
Retention
Section titled “Retention”Daily sweeps delete what is past its window.
| Variable | Default | Allowed values | Effect |
|---|---|---|---|
SIFT_EVENT_PAYLOAD_RETENTION_DAYS | 30 | Days | How long an event’s content is kept. |
SIFT_LABELLED_PAYLOAD_RETENTION_DAYS | 30 | Days | How long the content of an event that carries a training label is kept, from when it arrived; also the oldest agent history that can be imported. Models train on what is kept, so raising it gives them more history and keeps those events longer. In shadow every event is labelled. |
SIFT_FILTERED_EVENTS_RETENTION_DAYS | 400 | Days, not below SIFT_EVENT_PAYLOAD_RETENTION_DAYS | How long a filter’s Filtered events keep each record. A window shorter than the payload window is refused at the sweep: nothing is deleted and the worker log records filtered_events_reap_refused. |
SIFT_CANDIDATE_SCORE_RETENTION_DAYS | 30 | Days | How long bake-off candidates’ scores are kept. |
SIFT_POLL_LOG_RETENTION_DAYS | 14 | Days | How long the record of each poll (the activity timeline’s poll history) is kept. |
SIFT_ARTIFACT_RETENTION_DAYS | 30 | Days | How long a retired model’s stored files are kept after it leaves service. A restore from a backup older than this may name models whose files are gone. See Back up and restore. |
Compose variables
Section titled “Compose variables”These are read by Docker Compose, not by Sift. Put them in .env beside the SIFT_*
settings.
| Variable | Default | Effect |
|---|---|---|
SIFT_WEB_PORT | 8080 | The host port for the console and the API behind it at /api: the only port the stack publishes. With the TLS overlay it serves only a redirect to HTTPS and the health probe. |
SIFT_HTTPS_PORT | 443 | With docker-compose.tls.yml: the host port for HTTPS. See Serve over HTTPS. |
SIFT_TLS_DIR | ./tls | With docker-compose.tls.yml: the directory holding tls.crt (full chain, leaf first) and tls.key (unencrypted PEM). |
SIFT_EXTRAS | empty | Optional extras built into the backend image, and which image the stack runs. setfit builds and selects the -setfit image, the only one that can train the text_clf_setfit recipe: about 1.1 GB larger, with about 133 MB more weights staged on first start. Rebuild after changing it. |
SIFT_ENV_FILE | .env | The file every backend container reads its SIFT_* settings from. docker compose --env-file does not change this; set SIFT_ENV_FILE too. |
COMPOSE_FILE | compose’s own default | The compose files to use, colon-separated. docker-compose.yml:docker-compose.tls.yml makes HTTPS the default for the checkout. It replaces compose’s default list, so name docker-compose.override.yml in it too if you use one. |
POSTGRES_USER | sift | The Postgres user, and part of the database address compose gives Sift. |
POSTGRES_PASSWORD | sift | The Postgres password. Read by Postgres only when its volume is first created: to change it later, also change it inside the database (ALTER USER). make init generates one. |
POSTGRES_DB | sift | The Postgres database name. |
SEAWEEDFS_ACCESS_KEY | sift | The object store’s access key, given to Sift as its S3 credentials. |
SEAWEEDFS_SECRET_KEY | siftsiftsift | The object store’s secret key. Read on every start, so changing it takes a restart, and the old key stops working at once. make init generates one. |
SIFT_API_PORT | 8000 | Only with the development override (docker-compose.override.yml): the API published on loopback. |
SEAWEEDFS_S3_PORT | 8333 | Only with the development override: the object store’s S3 endpoint published on loopback. |
Compose sets the datastore addresses and the object-store credentials for Sift itself
from these: SIFT_DATABASE_URL, SIFT_REDIS_URL, SIFT_S3_ENDPOINT_URL,
SIFT_S3_ACCESS_KEY and SIFT_S3_SECRET_KEY. A value in .env for any of them is
ignored, because the compose file’s own wins. SIFT_S3_REGION (us-east-1) and
SIFT_S3_BUCKET_ARTIFACTS (sift-artifacts) suit the shipped object store; leave them
unset.
Development only
Section titled “Development only”SIFT_DEV_CORPUS_PROFILE, SIFT_DEV_CORPUS_SIZE, SIFT_DEV_VERDICT_ERROR_RATE,
SIFT_DEV_VERDICT_BIAS, SIFT_DECISIONS_OLLAMA_URL, SIFT_DECISIONS_OLLAMA_MODEL,
SIFT_PIPELINE_PLUGINS.