๐Ÿ”’Security & Compliance

The questions your IT team will ask, answered.

Multi-tenant by design. Audit-quality data. Modern infrastructure.

Synagogues aren't healthcare or finance, but they handle sensitive relationships, pastoral conversations, and member-directory data that absolutely must not leak. MyBimah is built on the same security primitives that ship in modern enterprise B2B platforms โ€” tenant isolation enforced at the query level, append-only audit log with database-level immutability, encryption at rest + in transit, and role-based permission tiering. Every read site filters defense-in-depth.

Tenant isolation enforced at every query

Every database read in MyBimah goes through a single helper called withTenant(). It resolves the tenant_id from the request's host (subdomain โ†’ tenants.subdomain lookup) and scopes every downstream query. Direct Drizzle queries outside this wrapper are forbidden and code-reviewed against. The handful of platform-admin queries that legitimately span tenants live in a separate /api/admin/* tree with explicit is_platform_admin gating.

  • Per-request tenant resolution via withTenant() โ€” same pattern across 113+ callsites
  • Anti-tamper: session payload's tenant claim must match the user's actual tenant_id; mismatch forces re-auth
  • Platform admin queries scoped to /api/admin/* with explicit is_platform_admin checks
  • Cross-tenant cookie tampering rejected even within the same parent domain

Audit log with database-level immutability

Every state-changing operation writes a row to audit_log with the actor, action, entity, label, and details. Database-level triggers REJECT any UPDATE / DELETE / TRUNCATE on audit_log โ€” even a compromised application user with full DB credentials cannot mutate the trail. Append-only, period.

  • audit_log_block_mutations() plpgsql function returns insufficient_privilege on UPDATE/DELETE/TRUNCATE
  • Three triggers (no_update, no_delete, no_truncate) BEFORE each operation
  • INSERT remains unrestricted โ€” auditing happens automatically, can't be "forgotten"
  • Retention + archival require dropping the trigger inside a controlled maintenance window (itself loggable)

Encryption + transport security

TLS everywhere. HTTPS-only via Let's Encrypt certs auto-issued per subdomain on Vercel. Postgres connections use sslmode=require. Session cookies are HMAC-signed (rotating AUTH_SECRET invalidates every session). OAuth state cookies signed + HttpOnly + SameSite=Lax. Sensitive env vars (OAuth secrets, AUTH_SECRET, RESEND_API_KEY) marked --sensitive on Vercel so they don't leak in build logs.

  • TLS 1.2+ enforced via Vercel edge
  • Postgres connection pooling with sslmode=require + Neon's per-connection auth
  • Session cookies HMAC-SHA256-signed; rotating AUTH_SECRET invalidates all sessions on that env
  • Password hashing: node:crypto.scrypt, 16-byte salt + 64-byte hash, constant-time compare

Authentication: Email + password, OAuth, invitations

Password sign-in with scrypt-hashed credentials. Optional Google + Microsoft OAuth (per-tenant configurable, supports Workspace + M365 + personal accounts). Email-based invitation flow with single-use 7-day tokens for new-user onboarding. Lockout after 10 failed attempts, 15-minute window. Audit-logged.

  • scrypt password hashing โ€” node built-in, no third-party dep
  • Failed login counter + soft lockout (10 attempts, 15-min window)
  • OAuth: Google + Microsoft, per-tenant or platform-admin host scoping
  • Email invitations: 256-bit random tokens, hash-stored, single-use, 7-day TTL
  • Email-confirmation linking on first OAuth sign-in (auto-link if email matches an existing tenant user)

Permission model + role-based access

15+ permissions tiered hierarchically: view < edit < manage / view < assign / view < attach. Higher tiers IMPLY lower tiers via a code-level expansion map called once per request at session-load. Role-based permission catalog editable by tenant admins; the Administrator role is auto-created at tenant provisioning with the full permission set + an anchor-permission lockout guard that prevents admins from removing their own access.

  • Permission expansion: services.edit โ†’ services.run_sage โ†’ services.view (one storage row, three effective permissions)
  • Anchor-permission protection: Administrator role can't remove tenant.system_roles.manage from itself
  • Per-route requirePermission() checks โ€” middleware-level + route-handler level
  • Per-permission UI gating: invisible affordances when the permission isn't held

Data residency + infrastructure

Hosted on Vercel (US East โ€” iad1). Postgres on Neon (also US East). Cloudflare DNS. Resend (Amazon SES under the hood) for outbound email. Sentry for error reporting. All vendors have published SOC 2 Type II reports. We're not SOC 2 ourselves yet โ€” when the launch-partner cohort matures, we'll start the audit. Data residency for non-US tenants is on the roadmap.

  • Vercel Pro plan โ€” production + qa + preview environments
  • Neon Postgres with point-in-time recovery + branch databases
  • Cloudflare DNS with wildcard subdomain support
  • Sentry error reporting with PII scrubbing โ€” emails, Hebrew names, password hashes never sent
  • Resend with verified mybimah.com sender + DKIM-signed

Privacy posture

Tenants are data controllers; MyBimah is a processor. Subprocessor list published on the Privacy page. Cookies kept to the operational minimum โ€” session HMAC + OAuth CSRF state. No analytics tracking pixels. Email tracking limited to Resend's delivery + bounce + complaint webhooks (which we use to keep email reputation healthy, not to fingerprint readers).

  • Tenant = data controller; MyBimah = processor
  • Sentry's PII scrubbing strips emails, Hebrew names, password hashes from error reports
  • No third-party analytics (no Google Analytics, no Mixpanel, no Heap)
  • Cookies: session, oauth_state โ€” that's it

Want a security questionnaire walkthrough? Send us your standard form.

We're onboarding new launch-partner synagogues selectively while we mature the platform with our beta partner. Send a note and we'll set up a conversation.