Encryption
- Payload encryption: XSalsa20-Poly1305 (libsodium secretbox)
- Record key: 32-byte random key per dataset
- Key wrapping: Argon2id (pwhash) from customer passphrase
- Passphrase is never stored — no back-door possible
Access tokens
- Invitation links: single-use token (32-byte random value)
- Only HMAC-SHA256 hash stored in the database
- Expiry: 7 days from issuance
- After first unlock: session-based access (30-min idle timeout)
Admin authentication
- No passwords — exclusively OTP via email
- OTP: 6 digits, valid 10 minutes, single use
- OTPs stored only as HMAC-SHA256 hash
- Rate limiting: max. 5 requests/hour per email, 20/hour per IP
Tenant isolation
- Full data isolation per tenant (tenant_id on all tables)
- All database queries are tenant-scoped
- No cross-tenant data access possible
Logging
- Separate audit log, security log, and app log
- No plaintext data, passphrases, or full email addresses in logs
- Daily log rotation (30-day retention)