Trust by design

The boring kind of safe.

Multi-tenancy enforced at the database layer, every secret encrypted at rest, every permission check on the server. No "we'll get to it after launch" entries on the security backlog.

Tenant isolation

Every customer gets its own Postgres schema. There is no shared "data" table behind a tenant_id filter — a query in tenant A literally cannot see tenant B's rows because the schema isn't on its search path.

django-tenants · schema-per-tenant · enforced at connection level

Encrypted at rest

Integration OAuth tokens, signup payloads and other sensitive fields are encrypted with Fernet (AES-128 in CBC + HMAC-SHA256). The keys live outside the database — even a stolen DB dump is inert.

cryptography.fernet · key rotation supported

Server-side RBAC

Permissions are checked at the API, not the UI. Every protected endpoint inherits a single mixin that filters querysets by the caller's resource × action × scope. The web client mirrors the rules for UX, but a forbidden API call never reaches the row.

DRF · ScopedQuerysetMixin · HasRolePermission

EU hosting

Data lives in EU regions (Belgium / Frankfurt). Backups, replicas, and worker queues never leave the EU. The application clock is Europe/Brussels and currency is € — both intentional, both relevant for procurement.

Belgian-incorporated · EU data residency · no US sub-processors for storage

Audit history

Every change to tasks, comments, time entries, attachments and tags is captured by django-simple-history. Queryable for compliance, exportable on Business. Tag adds / removes and viewer changes get their own audit events — not just diffs of the parent row.

retention: 90 d (Free) · 1 y (Pro) · 3 y (Business)

Account safety

Passwords hashed with Argon2 — the current state-of-the-art, winner of the Password Hashing Competition. Resets use a 6-digit OTP with a 15-minute TTL and a hard 5-attempt cap. Brute-force protection via django-axes; signup endpoints rate-limited per IP and per email domain.

Argon2 · django-axes · OTP TTL 15 min · 5-attempt lockout

Last-admin safeguard

The API refuses to deactivate the workspace's last admin, demote the last admin's role, or delete the last admin user — returning HTTP 409 with a clear error. The org-locking accident that takes down a workspace at 4 PM on a Friday literally cannot happen.

enforced server-side · tested in apps/roles/tests/test_roles.py

Documented API

Every endpoint is described by an auto-generated OpenAPI 3 schema at /api/schema/ with interactive Swagger UI and ReDoc. Our own TypeScript clients regenerate from the same schema, so the docs can't drift from the code.

drf-spectacular · Swagger UI · ReDoc · Orval-generated TS clients

GDPR

Aligned, documented, defensible.

  • Lawful-basis records per data category
  • Data Processing Agreement (DPA) on request
  • Right-to-export tooling (CSV/JSON dumps per tenant)
  • Right-to-erasure: schema drop + S3 prefix purge
  • Sub-processor list maintained & surfaced in the DPA
Roadmap

What's next.

  • SSO / SAML — Business + Enterprise (Q4 2026)
  • SOC 2 Type II readiness (2027)
  • Customer-managed encryption keys (Enterprise)
  • Pen-test report — annual cadence, available under NDA

Need a DPA, security questionnaire or sub-processor list?

Reach out via the contact form and we'll send the relevant pack for procurement.

Request the security pack