Security and privacy
Clatri handles your money, your health, your documents. That demands more than a login and a promise that "your data is safe". Clatri's security works in layers, and each layer solves a different problem. The right question isn't "does Clatri encrypt or not", but what each layer protects, what depends on the server, and what depends on you.
The layers
| Layer | What it solves |
|---|---|
| Authentication + 2FA | Who you are, with optional TOTP verification |
| RLS (Row Level Security) | What data belongs to you or was shared with you |
| Private storage | That files and documents are not publicly exposed |
| Disk encryption | Physical protection of infrastructure and backups |
| System encryption | Sensitive data the backend still needs to process |
| User encryption | Especially sensitive information, protected with your own key |
| Local protection | Secrets on the device and app lock |
They're not alternatives. They're complementary. If one fails, the others keep operating.
Where each thing lives
Your information is not all in the same place. Clatri intentionally separates the infrastructure into three independent zones:
- Your device keeps your session, the derived key, and local secrets in the operating system's secure storage. It never talks to the database directly.
- The backend lives on a separate server. It receives your requests over HTTPS and uses your JWT to connect to the database — meaning every query it makes is subject to the same RLS restrictions as if you made it directly.
- The database and storage live on infrastructure different from the backend. The backend has no free access to your data: it can only see and modify what your JWT allows under each table's RLS policies.
That they are separated matters: compromising one doesn't automatically grant access to the others. An attacker who breaches the backend doesn't have the data on disk without the database credentials. Someone with database access doesn't have the encryption keys that live in the backend. And someone with your device in hand faces the app lock and the OS secure storage.
Authentication
Your account is authenticated with a JWT (JSON Web Token) — a signed token that contains your identity and that the app sends on every request. The backend validates it before doing anything: if the token isn't valid or has expired, the request is rejected. But being authenticated doesn't mean accessing everything. That's controlled by the next layer.
Two-factor authentication (2FA)
From Settings > Security you can enable 2FA with TOTP (Time-based One-Time Password) — a 6-digit code that changes every 30 seconds and that only your authenticator app can generate. Works with Google Authenticator, Microsoft Authenticator, Authy or any TOTP-compatible app:
- you enable 2FA from settings and Clatri shows you a QR code
- you scan it with your authenticator app
- you confirm with a 6-digit code
- Clatri shows you recovery codes — save them somewhere safe, they're not shown again
From then on, every time you log in (with Google or Apple), after the normal login Clatri asks for the TOTP code before giving you full access. Your session starts at a basic security level and only steps up to full once you verify the code.
2FA is optional — you decide whether to enable it. But if you enable it and lose access to your authenticator app without the recovery codes, you won't be able to get back into your account. That's why it's important to save the recovery codes somewhere safe when you set it up.
RLS: database-level permissions
Clatri uses Row Level Security on PostgreSQL. Every row in every table has policies that evaluate, per query:
- your
user_id - the active entity
- whether you are owner or the entity was shared with you
- your permission level (owner, editor, viewer)
- the domains you have access to (finances, health, administration, chat)
It's not an application-level filter you can skip with a direct API call. It's a database-level restriction: if the policy says you can't read that row, PostgreSQL doesn't return it. It applies to both structured data and files in storage.
Private storage
Images and documents are saved in a private bucket, organized by entity and content type. There are no permanent public URLs. When you need to access a file, the path depends on whether the file is encrypted or not:
- Files without additional encryption (avatars, audio, generic attachments): the backend generates a signed URL valid for 2 hours. The app uses it to load the file directly from storage and caches it locally until it expires. After 2 hours, it asks for a new one.
- Encrypted files (health, finances, administration): never receive a signed URL. The app asks the backend for the file, which downloads it from storage, decrypts it in memory with the appropriate keys (system, user, or both), and returns the decrypted bytes directly. The encrypted file in storage is never exposed.
Disk encryption
The database and its backups live on infrastructure with disk-level encryption. If someone obtained physical access to the storage media, they wouldn't have the data in cleartext.
This layer protects the infrastructure. It doesn't decide who can read a record inside Clatri — that's RLS's job.
System encryption
System encryption exists for one concrete scenario: someone gains computational access to the database — not physical access to the disk, but access to the data itself — and still can't read users' sensitive information. The fields and files encrypted with this layer are unreadable without the system key, which lives in the backend, not in the database.
It's used for:
- financial data and documents
- personal administration data and documents
- certain identifying entity data
The implementation uses AES-256-GCM — authenticated encryption that protects both confidentiality and integrity. Each value is encrypted with a random 12-byte nonce, meaning encrypting the same text twice produces different results.
Since the key is managed by the backend and not the user, features like sharing an entity, reviewing documents, or running automations keep working without asking for an extra password at every step.
User encryption
User encryption exists for the most sensitive information — today, mainly health data and documents. The idea here is that unlocking doesn't depend solely on the server, but also on a key derived from your own password.
It's an optional layer. During onboarding Clatri no longer asks for an encryption password. The app starts with authentication, RLS, system encryption, and private storage, and that already protects your information. User encryption is enabled when you decide to add it, and applies from then on to the domains that need it most (today, health).
Your encryption password
If you want to enable user encryption, go to Settings → Security → Encryption password and configure it when ready. It's a credential separate from your login, with its own requirements: minimum 8 characters, uppercase, lowercase, numbers, and symbols.
When you configure it:
- the password travels over HTTPS to the backend
- the backend generates a random 16-byte salt and derives a 32-byte key with Argon2id
- the backend stores the salt and a verification token — the result of encrypting a known string with your derived key, so it can later confirm that a password reconstructs the correct key
- the backend returns the derived key to the app
- the app stores it in the OS secure storage
What's not kept on the server: not your password, not the derived key, not a decryptable copy of your data. Just the salt and the verification token.
Double layer
When the user key is available, protected data goes through two encryption layers:
- it's encrypted with your user key (AES-256-GCM)
- that result is encrypted again with the system key (AES-256-GCM)
The stored data is wrapped by both layers. Reading it requires both keys. Decrypting it walks the reverse path: first the system layer, then the user layer.
At runtime
The app doesn't send your password with every message to the agent. What it sends, when the operation requires it, is the derived key in the X-User-Encryption-Key header, inside the HTTPS/TLS connection. The backend uses it to decrypt or encrypt protected content during that specific operation.
Put another way:
- the password is used only for configuration, unlock, change, or reset
- the derived key is used for normal operations on protected content
New device
If you sign in from a device that doesn't have the derived key locally, and you have user encryption enabled, Clatri asks for your encryption password once at login. The backend validates it against the stored salt, re-derives the key, and returns it for the app to store locally. If you never enabled user encryption, this step doesn't appear.
Password change and reset
If you change the password knowing the previous one, Clatri rotates the key and re-encrypts all affected content — prescriptions, medical documents, health files.
If you do a reset without knowing the previous one, a new cryptographic configuration is generated, but content protected with the prior key stops being recoverable. That's not a bug: it's the direct consequence of unlock depending on a credential the server cannot reconstruct for you.
What gets encrypted and how
Not everything is encrypted the same way. The policy depends on the domain:
| Domain | Metadata | Files | Level |
|---|---|---|---|
| Health | System encryption (+ user if active) | System encryption (+ user if active) | Maximum |
| Finances | System encryption | System encryption | High |
| Administration | System encryption | System encryption | High |
| Other | No extra encryption | Private storage | Base |
In all cases, RLS and private storage apply. Encryption is the additional layer. The double layer (user + system) over health data only applies once you've enabled user encryption from Settings; before that, health is at system encryption like the rest.
Local protection on the device
The app keeps local secrets — the derived key, the encryption password, and migration metadata — in the operating system's secure storage:
- iOS/macOS: Keychain, with accessibility
firstUnlockThisDevice - Android: system-encrypted SharedPreferences
All values are stored with user-scoped keys, preventing cross-account access on the same device.
App lock
In Settings you can enable app lock. When enabled, if the app spends more than 5 minutes in the background, it locks and asks for local authentication to reopen:
- Face ID or Touch ID on Apple
- Fingerprint or biometrics on Android
- System PIN, pattern, or passcode as a fallback, if the platform allows it
This protects physical access to the device. It doesn't replace the encryption password — they're different layers.
Your data is yours
From Settings → Export data you can download your information in Excel files:
| Export | Contents |
|---|---|
| Health report | Conditions, episodes, prescriptions, medications, body metrics, medical events |
| Financial snapshot | Accounts, cards, recurring payments, obligations, assets, savings, investments, and a summary with net worth |
| Financial movements | Expenses and incomes by date range, with a breakdown by category |
You can export a single entity as an Excel file (.xlsx) or several entities as a ZIP file. There are no locks or waiting periods — your information belongs to you and you can take it whenever you want.
In short
Clatri's security doesn't depend on a single lock. It combines strict database-level permissions, private storage, layered encryption with AES-256-GCM, key derivation with Argon2id, and local protection on the device. If one layer is compromised, the others keep limiting access to your information.