Technical controls

The cryptographic, access-control, and operational primitives that protect tenant data day-to-day.

Authentication

  • Session auth uses an asymmetric RS256 JWT (15-minute access token + 7-day refresh token). Refresh tokens are stored as an HttpOnly, SameSite=Lax cookie scoped to /api/v1/auth/.
  • CSRF protection is enabled on every state-changing route via a double-submit cookie pattern.
  • Passwords are hashed with bcrypt; the cost factor is tuned for the production environment.
  • API keys are prefixed with lmn_, generated with a CSPRNG, and stored as a SHA-256 hash. Keys are shown once at creation and never displayed again — UI forces a copy-confirm before the dialog can close.

Authorisation

  • Per-tenant roles: owner, admin, member, viewer. Mutation endpoints check role; sensitive admin actions require the owner role.
  • Cross-tenant platform admin is a separate flag (users.is_platform_admin), distinct from the per-tenant role. Required for the operator dashboard at /admin/usage.
  • API keys carry granular scopes; only the minimum scope set is granted per key.

Multi-tenant isolation

  • PostgreSQL row-level security with FORCE ROW LEVEL SECURITY is enabled on every tenant-scoped table. The limena_app role does not have BYPASSRLS; a query without an explicit app.current_tenant_id returns zero rows.
  • Cross-tenant operator reads run as a separate limena_readonly / limena_maintenance role with audited access.

Encryption

  • In transit: TLS 1.2+ on every public endpoint. HSTS in production.
  • At rest: integration credentials (OAuth refresh tokens, third-party API keys) are encrypted with AES-256-GCM using an HKDF-derived per-tenant key 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

IP-keyed rate limits at the HTTP layer; per-tenant velocity caps for AI calls (default 500/hour); per-tenant monthly token ceilings (default 20M). Trips fail-soft with an in-app banner explaining the state.