Skip to content

Security

Isolation, secrets, uploads, and the boundaries that are enforced.

Enforced by the database, not by the application. Every protected table refuses a query scoped to another workspace, and the coverage is proved against a fixture row in every table, so a new table cannot be added without a decision about its isolation.

The API runs as a non-superuser role, because row-level security does not apply to a superuser. Which connection a query uses is what decides whether the isolation is real.

An httpOnly cookie holding a random token. Only its SHA-256 is stored. 30 days, revocable individually from Settings. A password change ends every other session.

SecretHow it is stored
Passwordsscrypt hash
API tokensSHA-256, with the prefix in clear for lookup
Session tokensSHA-256
Site passphraseHash only. There is nothing to read back.
Recovery codesHashed
TOTP secretEncrypted, because verifying a code needs it
Your identity provider’s credentialsEncrypted, per workspace

Every upload is validated before it reaches a store:

  • the declared Content-Type is ignored; the bytes decide the type;
  • 25 MB and 50 megapixels, so a decompression bomb cannot reach a reader’s browser;
  • SVG is rewritten against an allowlist of elements and attributes;
  • keys are content-addressed, so a filename never becomes a path and traversal is unreachable rather than defended against.

Anybody who can write documentation can write “ignore your instructions”: an importer, a contractor, a compromised account.

Passages sent to a model for Ask are delimited, numbered, and introduced as material to quote rather than as instructions to follow. Citations come from retrieval rather than from the model, so an invented source cites nothing.

HMAC-SHA256 over the timestamp and the body together, in a staleless-signature header, with the timestamp alongside it. Verify both, and reject a timestamp outside your tolerance. See Webhooks.

Verified over the raw bytes, in constant time, inside a five-minute window, before a single field of the body is read.

A target that resolves to a private network is refused, so a webhook cannot be used to reach inside the network Staleless runs in.

Published sites are served from a different registrable domain from the app.

Cookies are scoped to a registrable domain. A site on a subdomain of the app domain would share the app’s cookie namespace, and site pages contain customer-authored content. One cross-site scripting flaw in one customer’s page would become a session-theft path into the app for every user.

This is the same mitigation GitHub uses with githubusercontent.com.

Sign-in, sign-up, magic links, password resets and verification are all rate limited. So are reader feedback, Ask, Slack commands and the content API. The API limit is keyed on the token, so one caller cannot throttle another.

TOTP with recovery codes. An owner can require it for a whole workspace; enrolment is the gate rather than rejection, so turning it on does not lock anybody out mid-session.

Report a suspected vulnerability to your Staleless contact before disclosing it. An Enterprise agreement can include a security review and a DPA.