Skip to content

Security

  • Every non-public route requires a Bearer JWT. Missing or invalid means 401, before any handler runs.
  • Tokens are signed with AUTH_JWT_SECRET (HS256), and the algorithm is pinned — an alg: none token is rejected.
  • Signatures are verified in constant time, before the payload is parsed.
  • Default lifetime is 7 days. There is no refresh token and no server-side session: expiry means signing in again.
  • Passwords are hashed with a memory-hard algorithm and a per-user salt. The hash never leaves the server and is never returned by any route.
  • Password reset tokens are short-lived and single-use; changing a password invalidates any outstanding link.

Two roles:

Role Scope
user Their own watches, runs, feedback, trust rules and devices
admin The above, plus key and model configuration

Ownership is enforced on every read and write. Another owner’s resource returns 404, not 403, so the API does not confirm that it exists. When creating or updating a watch, the owner comes from your token — an ownerId in the body is ignored.

  • Secrets enter in one place, validated at startup. Nothing else reads the environment.
  • Stored API keys are encrypted at rest with authenticated encryption; the master key exists only in the environment, never in the database.
  • Keys are decrypted in memory at the point of use, and the API returns only masked previews.
  • Every key-configuration write is audited with the masked preview, never the value.
  • Watcher never logs a secret. Provider failures are reported by status code.
Endpoint Public? Reveals
/health Yes That an instance exists
/config/status Yes Which integrations are configured, as booleans — never values
/auth/register Yes Open self-signup is enabled by default
/auth/login, /auth/forgot-password, reset and verify Yes Nothing about which accounts exist
Everything else No

In-memory fixed windows, per process: 20/minute per IP on auth routes, 10/minute per owner on AI routes, and 10/minute overall on key-probing admin routes so the endpoint cannot be used to test stolen keys at speed. Windows reset on restart, and are per instance rather than shared.

Collected page content is treated as data, never instructions. The evaluator is explicitly told to ignore text inside a page that tries to change its task or claim something is confirmed. The real defences are structural: the quote must exist in the collected text, the link must be one Watcher fetched, and an ungraded domain cannot produce a confirmed alert. See Accuracy.

Content Watcher collects is sent to the model provider you configured, along with your condition. Nothing else leaves your infrastructure except the alerts you asked for.

Free second-opinion tiers may use submitted prompts for training. For a watch over private sources, set allowSecondaryModel: false — it then runs primary-only, with no second opinion and no failover.

Terminate TLS at your host. The API sends CORS headers only for origins listed in WEB_ORIGIN; with none configured it sends none at all. There is no wildcard mode.

  • AUTH_JWT_SECRET is high-entropy and at least 32 characters
  • CONFIG_ENCRYPTION_KEY is set and backed up
  • WEB_ORIGIN lists only origins you control
  • The database restricts network access to known addresses
  • Secrets live in a platform secret store, not in files on disk
  • You have decided, deliberately, whether the API is publicly reachable
  • Database dumps are stored somewhere private
  • Admin accounts are limited to people who need key access