PayStub

Security

PayStub stores your company's financial and PII data. This page documents what we protect and how. We update it when we change something material.

Data we collect

Encryption

AES-256-GCM at the column level for SSN-last-4, EIN, bank routing, bank account, MFA secrets, and recovery codes. The Additional Authenticated Data is bound to model:field:rowId so ciphertext is non-portable across rows. All data in transit is TLS 1.2+ (TLS 1.3 preferred). The database uses TLS-only connections.

Today the master key lives in a server env var. A KMS migration (AWS KMS envelope encryption) is on our roadmap before SOC 2.

Multi-tenant isolation

Every row carries a company_id. A Prisma client extension auto-injects the active tenant into every read and write — calls outside a tenant context throw and never return rows. We have an integration test that proves Tenant A queries return zero rows even when asked for Tenant B's row by id.

Authentication

Step-up authentication

Sensitive actions (issuing payroll, editing bank info, changing MFA, requesting account deletion, changing the Owner) require recent re-authentication — within 30 minutes of activity. Otherwise the server returns a 401 step-up challenge.

Audit log

Every write on a whitelist of payroll-relevant entities — companies, members, workers, bank accounts, tax profiles, deductions, pay runs, paystubs — is logged with the before/after diff, the acting user, the IP, the user-agent, and the request id. Encrypted columns are redacted to "<encrypted>".

Backups + retention

Compliance roadmap

Reporting a vulnerability

Email [email protected] with details. We acknowledge within 1 business day and aim to resolve high-severity issues within 7 days. Coordinated disclosure: please give us 90 days before publishing.