Skip to content

Security and data handling

Sift holds an API key into your Kindo tenant and decides, on its own, that some events do not need an agent run. So the credential is worth protecting and the decisions are worth making hard to corrupt. This page describes how Sift handles accounts, secrets and data, what it sends out of the deployment, how long it keeps what it stores, and what it does not do.

  • Passwords are hashed with bcrypt at cost 12.
  • A password must be at least 12 characters. One longer than 72 bytes is refused rather than silently cut short, which is what bcrypt would otherwise do.
  • A sign-in for a username that does not exist takes as long as one that does, so timing does not reveal which accounts exist.
  • The first admin account is seeded from SIFT_DEFAULT_ADMIN_USERNAME and SIFT_DEFAULT_ADMIN_PASSWORD (admin/admin by default). While any active account still has the shipped password, the console shows a banner that cannot be dismissed and the system status raises a critical warning. Production mode refuses to start with the shipped password in .env.
  • There is no single sign-on, no multi-factor authentication and no password complexity rule beyond the minimum length.
  • A session is a signed token (HS256, with a key derived from SIFT_SECRET_KEY) in a cookie named sift_session: httpOnly, SameSite=Lax, and Secure everywhere except development mode.
  • It lasts SIFT_SESSION_TTL_HOURS (12) unless ended sooner.
  • Sessions end on the server. Signing out ends every session of that account, on every device. Changing the password ends every other one. An admin changing an account’s role, disabling it or re-enabling it ends all of its sessions.
  • No single session can be ended on its own.
  • Every request that changes something must carry the session’s CSRF token in the X-CSRF-Token header. The token is bound into the signed session, so it cannot be forged without the signing key.

Failed sign-ins are counted in Redis over a 15-minute window: 10 per username and 30 per client address. Either limit refuses further attempts until the window passes. A wrong current password on the change-password form counts against the same username limit. There is no permanent lockout, which would let anyone lock a named operator out.

The client address is the one your trusted proxy recorded, chosen by SIFT_TRUSTED_PROXY_HOPS. See Serve over HTTPS.

Sift has three roles, and the server enforces them on every API route, whatever the console shows.

RoleCan
AdminEverything an operator can, plus users, the Kindo connection, which recipes new filters may use, webhook endpoints and the decision model.
OperatorCreate and run filters, train, and approve gold sets and thresholds.
ReviewerLabel gold sets and answer the verdict check, and read everything else about filters.

Approvals need an operator or admin; the server refuses a reviewer. See Users and roles.

SecretHow it is kept
The Kindo API keyEncrypted (Fernet) with a key derived from SIFT_SECRET_KEY. No response ever returns it; the console shows its last four characters.
Each webhook endpoint’s URL, optional header value and signing secretsEncrypted the same way, with a separately derived key. No response returns them.
Passwordsbcrypt hashes only.
SIFT_SECRET_KEY, SIFT_METRICS_TOKENOnly in .env on the host. Not in the database or any backup.

Changing SIFT_SECRET_KEY makes every stored secret unreadable. Decryption fails loudly rather than returning garbage, and an admin must enter the secrets again.

The secrets are decrypted in memory when used, so anyone who can run code in a backend container can read them. Encryption protects the database and its backups, not the running process.

Every log line passes through a filter that removes values whose key looks like a secret (password, token, API key, authorization, cookie and similar), as a backstop.

  • One published port. Only sift-web is published. The API and the object store are reachable only inside the Compose network.
  • Response headers. Every response carries X-Content-Type-Options: nosniff, X-Frame-Options: DENY and Referrer-Policy: no-referrer, and over HTTPS Strict-Transport-Security.
  • The object store refuses anonymous requests. It always runs with the key from SEAWEEDFS_ACCESS_KEY and SEAWEEDFS_SECRET_KEY, and its admin UI and WebDAV are off.
  • Metrics are off unless SIFT_METRICS_TOKEN is set, and then need that token.
  • Inside the stack, traffic is plain. There is no TLS between nginx, the API and the datastores.
  • Production mode refuses published or weak credentials at start. See the production checklist.

Events, tickets and agent transcripts carry text written by others, including attackers. Sift reads them into model prompts when Sift’s assistant proposes a filter, when a gold set is built and when an agent is analyzed. So:

  • That content enters a prompt only inside a delimited data block, never as instructions.
  • Sift’s assistant and the analyzer have no tools that change anything. They propose; a person applies.
  • Their answers must fit a fixed schema, so an injected instruction has no field to act through.
  • The console shows such content as plain text, with no HTML, Markdown or links rendered.
  • Long content is cut: each event quoted in a gold-set build is at most SIFT_GOLD_MAX_EVENT_CHARS (2,000) characters.

This limits what injected text can do. It does not stop it from arguing for a bad filter; the person who approves the gold set and the threshold is that defence.

DestinationWhenWhat it carries
Your Kindo APIOnce an admin connects KindoSource reads (tool name, parameters, watermark). Triggers: the event itself, up to SIFT_MAX_TRIGGER_INPUT_CHARS (32,000) characters, and nothing about its score. Model calls for Sift’s assistant, gold-set builds and agent analysis: sampled events, agent transcripts and what a person types. Reads of agents, runs, conversations, models and integrations.
Webhook receiversOnly if an admin adds one, up to tenThe alert itself: Sift’s own sentence about the condition, its code, the filter’s id and state, and a link. Never event content, scores, verdicts or the filter’s intent. The filter’s name only if Include filter names is on for that endpoint. HTTPS only outside development, signed, and checked against private and metadata addresses at every send.
The model download base URLFirst start, unless the weights are stagedNothing from your data: a download of the pinned weights.
TypeSafe’s Jev APIOnly with SIFT_JEV_ENABLEDThe fields a filter’s Decide stage reads, at most SIFT_DECISIONS_MAX_STATE_CHARS (4,000) characters per event. See The decision model.

Sift sends no telemetry, and the object store’s telemetry is switched off. What Kindo keeps of what it is sent is governed by your tenant, not by Sift.

Sweeps that run daily or nightly enforce these windows.

DataKeptSetting (default)
Event contentCleared after the window; the event’s record and scores remainSIFT_EVENT_PAYLOAD_RETENTION_DAYS (30)
Content of events that carry a training labelThis long from arrival instead. In shadow every event is labelled.SIFT_LABELLED_PAYLOAD_RETENTION_DAYS (30)
Filtered events (which event, its score, the model, the time; never content)Deleted after the window. Must be at least the payload window.SIFT_FILTERED_EVENTS_RETENTION_DAYS (400)
Poll logDeleted after the windowSIFT_POLL_LOG_RETENTION_DAYS (14)
Bake-off candidate scoresDeleted after the windowSIFT_CANDIDATE_SCORE_RETENTION_DAYS (30)
Files of retired modelsDeleted this long after retirementSIFT_ARTIFACT_RETENTION_DAYS (30)
Trigger attempts30 daysfixed
Webhook deliveries and cleared alerts30 daysfixed
Assistant conversations, with their sampled events (Redis)8 hours after the last changefixed

Kept with no window: scores, labels, agent runs and what each agent returned, the audit log, gold sets with the events their reviewers saw, and analysis reports. Some of these hold copies of event content that the payload windows do not reach. Filters are retired, never deleted. Backups keep whatever the database held when they were taken.

  • Single sign-on, multi-factor authentication or an external identity provider.
  • A Content-Security-Policy header.
  • Encryption at rest beyond the stored secrets: Postgres, the object store and backups are the host’s to encrypt.
  • TLS between its own containers.
  • Certificate issuance or renewal.
  • More than one tenant. Sift is built for one organization behind a trusted network perimeter.