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.