Security
Encryption and secrets
Last updated · October 7, 2026
How tokens, keys and passwords are encrypted, and what bounds their use.
Encryption and secrets
| Layer | How |
|---|---|
| In transit | TLS 1.2 and 1.3 only, with HSTS. Earlier versions are refused and session tickets are disabled. Internal services talk over a private network with no published port. |
| Integration secrets | Every token is encrypted with AES-256-GCM before it is written, into a column separate from the readable configuration. GCM is authenticated encryption: a random 96-bit initialisation vector per record and a 128-bit tag, so tampering with stored ciphertext fails at decryption instead of being silently accepted. |
| Master key | Held in HashiCorp Vault under a Shamir seal and loaded into memory when the service starts. It is not in the source, the configuration, the container images or the logs. |
| At rest | Database encrypted at rest on OVHcloud. Daily backups, encrypted on the server before they leave it, then held in encrypted object storage spread across three availability zones in a French region separate from the production site, with versioning enabled. Restores are verified from that storage rather than assumed. |
| Directory records | The full directory record synced from your identity provider is encrypted with the same scheme before it is stored. |
| Lutril account passwords | Argon2id with 64 MiB of memory and 3 iterations, plus a server-side pepper held outside the database. Not reversible. |
- Tokens never come back out. No API response, no log, no export and no screen shows one, not truncated, not even to an administrator of your own workspace.
- Least scope. We ask for read-only identity scopes first. A write scope is requested only where a feature you switched on requires it, such as creating or revoking an account.
- Revocation works from either end. Disconnect the integration in Lutril and the secret is destroyed, or revoke the authorisation in the vendor's console and we lose access without needing to act.
- Outbound calls are validated before they are made. A URL you supply is checked against an allowlist of globally routable addresses, and the resolved address is pinned for the life of the connection, so a DNS answer cannot change between the check and the call. Credentials are stripped on a cross-origin redirect.