Security model
This page summarises the design documents in docs/security/ of the repository. It is not an audit or a certification, and not legal advice.
Trust boundaries#
| Part | Trust | Why it matters |
|---|---|---|
| Visitor's browser | Runs the loader; untrusted by the server | Only public data is ever sent to it |
| Delivery plane (your site, CDNs) | Public static files; can be tampered with in transit or at rest by an attacker with storage access | So every manifest is signed and every language file is hash-checked |
| Control plane | Holds secrets, hashes, translations | Authenticated by secret keys; tenant-isolated |
| Translation providers | Receive page text only | See Data handling |
Integrity of what visitors receive#
- The manifest carries an Ed25519 signature over its RFC 8785 canonical form. The loader and plugins refuse a missing or invalid signature, an unknown
kid, or a lowerversionthan they have already accepted. - The signed manifest lists each language file's SHA-256 and size. A file that does not match is refused.
- Rotation: several public keys can be configured. A valid manifest signed by a later key supersedes state created by an earlier key, so a stolen or buggy key can be recovered from if a standby key was configured in advance. Details and limits: Signing and key epochs.
- The signing key is held by the application (encrypted at rest) for development and first launch, with a move to a KMS or vault before scale. Keys are per site, so theft of one key affects one site.
Secrets#
- Secret API keys are shown once and stored as HMAC hashes with a pepper. They are compared in constant time and never logged.
- A site key and the public signing key are public by design.
- Do not put secrets in HTML, plugin option pages that print to the front end, or repositories.
Tenant isolation#
Every query is scoped to the tenant of the calling key and fails closed. Cross-tenant ids return 404.
Outbound fetches#
Crawling (POST /v1/sites/{siteId}/scan) is protected against server-side request forgery: the design refuses private, loopback and link-local addresses and requires connecting to the address that was validated (to defeat DNS rebinding). The review that specifies this is docs/security/SSRF_REVIEW.md; whether the final fetcher implements every point is verify.
Data handling#
- Page text sent to a translation engine: the text, the source and target language, and an optional short context label. Never keys, tenant ids, emails or IPs.
- Visitors: delivery logs hold IP address, user agent and
Origin. Retention is short by design; the exact period is an open decision. - Roles under GDPR (customer as controller, us as processor for site content) are a working assumption to be confirmed with counsel. A data processing agreement and sub-processor list are not published yet.
What this model does not claim#
- No third-party penetration test has been done.
- No compliance certification exists.
- Hosting regions and the EU data-residency option are not decided.
See also CSP and headers and Limits.