ModarhemModarhem
Back to Home

Security & Encryption

Last updated: April 20, 2026

Modarhem is built for Saudi businesses handling regulated financial data — ZATCA invoices, payroll, customer records. This page documents exactly how we protect that data, what's encrypted, and what isn't. Transparency beats marketing.

1. Encryption at Rest

Production data lives in a DigitalOcean managed PostgreSQL 17 cluster in Frankfurt. The underlying block storage is encrypted with AES-256 (LUKS) at the hypervisor layer. Daily snapshots are encrypted the same way and stored in DigitalOcean Spaces with server-side AES-256 encryption. Keys are managed by DigitalOcean and rotated on their standard cadence — we never hold the disk-encryption keys.

2. Encryption in Transit

Every request to modarhem.com terminates at Cloudflare with TLS 1.3 (minimum TLS 1.2 for legacy clients) and HSTS preloaded. Cloudflare-to-origin traffic is re-encrypted via nginx with a Let's Encrypt certificate. Internal services (API ↔ PostgreSQL, API ↔ Valkey, API ↔ MeiliSearch) bind to localhost or the private VPC interface and never traverse the public internet.

3. Application-Layer Secret Encryption (AES-256-GCM)

Sensitive credentials and PII stored in the database — TOTP/MFA secrets, ZATCA private keys, WhatsApp Business access tokens, card-network API keys, webhook signing secrets, bank IBANs (on bank accounts, employee payroll records, and affiliate partner profiles), and employee national ID / Iqama numbers — are wrapped in AES-256-GCM before insert using a key held in the server's environment file (chmod 600, outside the git tree). Each ciphertext carries its own random 12-byte IV and 16-byte authentication tag. Values are tagged with the "enc:" prefix so we can migrate any legacy plaintext forward safely.

4. Password Hashing

User passwords are never stored. We use Better Auth with scrypt as the password hash — memory-hard, slow, and resistant to GPU attacks. Salts are per-user and 16 bytes. The hash alone reveals nothing useful to an attacker who obtains the user table.

5. Payment Card Data

We never see your customers' card numbers. All subscription payments and (where enabled) customer checkouts are tokenized by Moyasar, a PCI-DSS Level 1 service provider. We store only the last 4 digits and the brand for UI display. No PAN, no CVV, no expiry dates.

6. Multi-Tenancy Isolation

Every tenant-scoped table carries an organization_id column enforced by PostgreSQL Row-Level Security (RLS) in FORCE mode. Each request sets app.current_organization_id via set_config within its transaction; the policy filters every SELECT/INSERT/UPDATE/DELETE against that value. Cross-tenant leakage is prevented at the database layer, not the application layer — so a bug in application code cannot expose another tenant's data.

7. Role-Based Access Control + MFA

Organizations have 32 built-in roles ranging from viewer (level 1) to owner (level 10), with per-module action overrides and per-user fine-grained overrides. Sensitive operations — plan changes, cancellation, permission-matrix edits, retention adjustments, VAT exemption changes, WhatsApp setup — require re-authenticating with a TOTP challenge before they complete. MFA enrollment is available to every user; organization owners can require it.

8. Session Security

Sessions ride HTTP-only, Secure, SameSite=Lax cookies signed with a server-side secret. Sessions expire after 30 days of inactivity and are revocable from the user's settings page. We do not issue long-lived refresh tokens to browsers.

9. Audit Trail

Every write that changes business-relevant state — invoice issue, payment record, user invite, permission change, plan adjustment — writes an immutable row to audit_log with actor, IP, old values, and new values. Retention defaults to 84 months (ZATCA-aligned). Merchants can adjust retention in settings, gated by MFA and a 30-day grace period before any deletion. A monthly worker enforces the retention cutoff with per-org audit entries so the act of retention itself is auditable.

10. Current Limits — Transparency Gap

We are honest about what's not yet encrypted at the application layer. Customer and supplier display data — names, phone numbers, email addresses, billing addresses — remains plaintext because it is constantly searched, filtered, joined, and printed on invoices. Encrypting it would either break core workflows or require deterministic encryption (which leaks equality). This data is protected by at-rest disk encryption, in-transit TLS, and RLS. If you need stronger isolation for a specific customer (for example an NDA-bound contract holder), we recommend using a pseudonymous display name and keeping the true identity outside the system. We would rather document the tradeoff than pretend it doesn't exist.

11. Incident Response

If we detect a security incident that affects your data, we will notify you by email within 72 hours of confirmation. If you believe you have found a vulnerability, email [email protected] — we read every report, respond within 2 business days, and credit responsible disclosure.

12. Regulatory Alignment

Modarhem is built to meet Saudi Arabia's Personal Data Protection Law (PDPL — Royal Decree M/19) and ZATCA's Phase 2 e-invoicing requirements. This includes data-subject export (the Export Everything bundle on the Settings page), retention controls, audit trails, and Saudi-region hosting. We do not transfer personal data outside the Kingdom except to named subprocessors listed in our Privacy Policy.

Questions about our security posture? Email [email protected]. For general privacy inquiries, see our Privacy Policy.