Authentication
Authentication
- session_tokens
- RS256 JWT · 15-min access, 7-day refreshRefresh tokens are stored as an HttpOnly, SameSite=Lax cookie scoped to
/api/v1/auth/. - csrf
- double-submit cookie on every state-changing route
- passwords
- bcrypt, cost tuned for production
- api_keys
- lmn_ prefix · CSPRNG · stored as SHA-256Shown once at creation and never displayed again; the UI forces a copy-confirm before the dialog can close.
Multi-tenant isolation
- PostgreSQL row-level security with
FORCE ROW LEVEL SECURITYis enabled on every tenant-scoped table. Thelimena_approle does not haveBYPASSRLS; a query without an explicitapp.current_tenant_idreturns zero rows. - Cross-tenant operator reads run as a separate
limena_readonly/limena_maintenancerole with audited access.
Encryption
Encryption
- in_transit
- TLS 1.2+ on every public endpoint; HSTS in production
- at_rest
- AES-256-GCM, HKDF-derived per-tenant keysIntegration credentials (OAuth refresh tokens, third-party API keys) are encrypted on top of a master key loaded from environment secrets. The plaintext value never persists.
Audit
Every mutation and every security-relevant event (login, logout, password change, API key creation/deletion, integration connect/disconnect, AI quota trip/reset) writes to audit_logs. The log is append-only at the application layer (the limena_app role has no UPDATE or DELETE privilege on the table) and exportable to CSV from Settings → Audit log.
Rate limiting + abuse
Limits
- http
- IP-keyed rate limits
- ai_velocity_cap
- 500 calls/hour per tenant (default)
- ai_token_ceiling
- 20M tokens/month per tenant (default)
Trips fail-soft with an in-app banner explaining the state.