Security
Isolation, secrets, uploads, and the boundaries that are enforced.
Security
Section titled “Security”Workspace isolation
Section titled “Workspace isolation”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.
Sessions
Section titled “Sessions”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.
Secrets
Section titled “Secrets”| Secret | How it is stored |
|---|---|
| Passwords | scrypt hash |
| API tokens | SHA-256, with the prefix in clear for lookup |
| Session tokens | SHA-256 |
| Site passphrase | Hash only. There is nothing to read back. |
| Recovery codes | Hashed |
| TOTP secret | Encrypted, because verifying a code needs it |
| Your identity provider’s credentials | Encrypted, per workspace |
Uploads
Section titled “Uploads”Every upload is validated before it reaches a store:
- the declared
Content-Typeis 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.
Reader content is untrusted
Section titled “Reader content is untrusted”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.
Webhook signatures
Section titled “Webhook signatures”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.
Slack signatures
Section titled “Slack signatures”Verified over the raw bytes, in constant time, inside a five-minute window, before a single field of the body is read.
Webhook targets
Section titled “Webhook targets”A target that resolves to a private network is refused, so a webhook cannot be used to reach inside the network Staleless runs in.
Site separation
Section titled “Site separation”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.
Rate limits
Section titled “Rate limits”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.
Two-factor authentication
Section titled “Two-factor authentication”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.
Reporting a problem
Section titled “Reporting a problem”Report a suspected vulnerability to your Staleless contact before disclosing it. An Enterprise agreement can include a security review and a DPA.