Tenant isolation
Every tenant-scoped database table runs Postgres FORCE ROW LEVEL SECURITY with WITH CHECK. The runtime database role is NOSUPERUSER NOBYPASSRLS. Cross-tenant data access is prevented at the database layer, not the application layer. Cross-tenant isolation is verified by an integration test suite that runs against a real Postgres instance via Testcontainers.
Two-role Postgres
We separate migration permissions from runtime permissions. Migrations run as the rekomi superuser via an explicit connection string. Runtime queries run as rekomi_app, which cannot bypass RLS. This means a SQL injection in application code cannot read another tenant's data.
Webhook security
Stripe webhooks are verified with EventUtility.ConstructEvent and a 300-second tolerance window. Replays beyond the window are rejected. Every processed event ID is recorded in billing_events with a UNIQUE index, so duplicate deliveries are no-ops. Clerk webhooks use Svix signature verification with the same replay protection.
Encryption
Data at rest: AES-256 (managed by the hosting provider). Data in transit: TLS 1.3 enforced. API key signing secrets are encrypted at rest via ASP.NET Core Data Protection with persistent keystore. Field-level encryption of tax form identifying fields is a planned hardening step.
Authentication and access
Authentication via Clerk (SOC 2 Type 2 certified). Session JWTs are verified on every API request. SAML SSO is on the enterprise roadmap.
Audit logging
Every mutation in the admin app is recorded to an append-only audit_log table per organization. Captures actor, action, target, timestamp, and IP. The log is searchable from the admin app and exportable as CSV.
Reporting a vulnerability
See our security disclosure page for how to report a vulnerability. We acknowledge within 24 hours and remediate critical issues within 7 days.
Sub-processors and data residency
Full sub-processor list at /legal/subprocessors, with data residency details in our trust center.