Tenancy boundary
Every Postgres table that stores tenant data has Row Level Security (RLS) enabled. Reads filter by is_team_member(team_id); writes also gate on owner self-check. We run a continuously-failing-then-passing RLS test suite (tests/unit/rls.test.ts) that exercises 30+ cross-tenant scenarios against a live Postgres instance. The cross-tenant SELECT leak introduced on 2026-05-08 (and closed on 2026-05-16) was caught by this suite.
Encryption
- At rest: AES-256 via Supabase / AWS RDS encryption.
- In transit: TLS 1.3.
- HSTS:
max-age=63072000; includeSubDomains; preload— submitted to the Chromium preload list (typical ingestion 1–4 weeks per Chromium release). - OAuth tokens (Google Calendar, Gmail, Contacts) are encrypted at the application layer with a separate key (
TOKEN_ENCRYPTION_KEY) before persisting.
Content-Security-Policy
Enforced (not Report-Only) on production as of 2026-05-12. The policy denies all iframe ancestors (no clickjacking), allows scripts only from self + Vercel Live + PostHog, and blocks any http:// sub-resources via upgrade-insecure-requests. CSP violation reports stream to /api/csp-report and into our log pipeline so we catch new third-party inclusions before they break.
AI provider chain
We deliberately chose AI providers whose terms forbid training on customer inputs:
- Cloudflare Workers AI — primary. No training on inputs. PDPA-safe.
- Groq — secondary. No training on inputs by default.
- Cerebras — tertiary. No training on inputs.
- DeepSeek — paid fallback. Configured with
store: false. - OpenAI — emergency-only. Configured with
store: falseand reserved for catastrophic fallback.
Gemini is intentionally disabled in production due to free-tier training-data clauses. PII (email, phone, physical address) is never sent to AI providers — only name, title, company, and the user's typed meeting notes.
Rate limits + AI quota
Every AI endpoint enforces a per-user daily cap (see docs/runbooks/ai-quota.md) plus a per-IP hourly cap on public renderers. Quota responses return HTTP 429 with a Retry-After header. DB errors fail-closed — we'd rather make you retry in 5 seconds than silently drain quota.
Audit log
Role changes (owner / admin / rep) write append-only rows to role_audit_log via a SECURITY DEFINER trigger. There is no DELETE policy on the table — even the service-role can't quietly erase audit history.
Subprocessors + listings
Full list: /subprocessors. Key infra: Supabase (Postgres + Auth + Storage + Edge Functions), Vercel (hosting + CDN + edge functions), AWS us-east-1 (underlying), Stripe (billing), Resend (transactional email), Sentry (error monitoring), PostHog (product analytics).
Reporting a vulnerability
Email security@vibeid.link with the details. We honor good-faith research — no legal action for testing that respects user data and doesn't violate uptime. Also see /.well-known/security.txt for the machine-readable disclosure policy.
What we don't yet have
For full transparency, in 2026-05 we don't yet have:
- A completed SOC 2 Type II report. Audit is in progress; the Enterprise plan ships with a DPA in the interim.
- A bug bounty program. Email disclosures are reviewed and acknowledged within 72 hours.
- Customer-managed encryption keys. Available on request for Enterprise engagements.