Security and Privacy
Navaid processes confidential company strategy, transcripts, assessments, documents, contact data, and AI-provider traffic. Security controls are layered and must survive direct API calls, cross-tenant identifiers, retries, and provider failures.
Trust Boundaries
| Boundary | Trust rule |
|---|---|
| Browser | Untrusted; anon key is public |
| RLS/RPC | Authoritative for direct Supabase access |
| Edge Function service client | Bypasses RLS; must explicitly authorize |
| LiveKit agent | Service-role host; validate room/session/speaker |
| Gemini/Resend/LiveKit/R2/Sentry | External processors; minimize data/secrets |
| Public token endpoints | Capability scoped, rate-limited, non-enumerating |
Authentication and Tenant Isolation
- Direct data requests use RLS against Auth JWT.
- Edge Functions validate with
auth.getUser. - Active company membership is required.
company_adminandcompany_consultantare admin-equivalent.- Session/document/task IDs are rebound to an authorized company.
- Selected client company is never authority.
- All functions deploy with gateway verification off and own auth decisions.
Database Defense
Controls include:
- RLS and least-privilege grants
- pinned-search-path SECURITY DEFINER helpers
- revoked implicit PUBLIC function execution
- column guards for company budgets, session completion and derived profiles
- forced user-message attribution
- append-only history/audit paths
- invitation domain trigger
- claim locks for turns and summaries
- idempotent invite consumption
- R2 quota/key guard functions
Service-role-only tables use RLS with no client policy.
Public Capabilities
Invitations
Anonymous preview is get_invitation_by_token(token), not table-wide SELECT.
Acceptance requires authenticated matching email.
Session shares
High-entropy, revocable, expiring tokens return an allowlisted read-only summary. Minting requires session owner/admin access.
Signup preflight
Responses are deliberately uniform so callers cannot learn whether an email exists or has an invitation. Buckets combine trusted IP and hashed email.
Early access
Public registration is validated and rate-limited; it does not expose listing.
MCP
Discovery/challenge is public; tools require OAuth bearer and tenant/role scope.
Secrets
Never expose:
- Supabase service role
- Gemini key
- LiveKit API secret
- R2 secret
- Resend key
- Sentry source-map token
The service role is valid only in Edge Functions, the isolated LiveKit worker,
and Node-side test/setup. It must never have a VITE_ prefix.
Application-email URLs come only from APP_URL. Client-provided hosts enable
branded phishing and secret-token theft.
Browser Security Headers
frontend/public/_headers enforces:
- CSP
- HSTS
X-Frame-Options: DENYX-Content-Type-Options: nosniff- strict-origin referrer policy
- microphone-only permissions policy
- COOP
Referrer policy is particularly important for invite/share/OAuth tokens.
Input and Output Safety
- method/body/length/type validation before provider work
- generic client 500s; detailed logs server-side
- no raw model output trust
- JSON/schema validation
- grounded URL/fetch controls
- document decompression/page/character limits
- safe image extension/MIME/size checks
- no model-driven eval or arbitrary DOM selector
- sanitized/escaped email templates
Abuse and Cost Controls
- authenticated per-user rates
- trusted gateway IP for public rates
- monthly input/output budgets
- bounded grounded fan-out
- conservative Live usage accounting
- R2 file/count/byte quotas
- single-winner claims
- idempotency ledgers
The Admin UI stores daily_token_budget and alert_config, but current main
does not consume them for runtime enforcement/alerts.
Privacy and Observability
Sentry:
sendDefaultPii:false- replay masks all text
- replay blocks all media
- raw production error messages are hidden from users
Do not log:
- prompts/transcripts/documents
- bearer, invite, share or OAuth tokens
- presigned URLs
- full email content
Provider payloads should include only the minimum company context required.
Account Lifecycle
User export should include user-owned/profile/session-related data appropriate
to the product. delete-account verifies self identity and uses transactional
cleanup for public rows before deleting the Auth identity.
Deletion must consider:
- ownership versus shared company records
- audit/history actor nulling
- invitation/referral/feedback references
- Storage avatar
- R2 documents governed by company ownership/retention
- provider logs and configured retention
Security Testing
Test at least:
- unauthenticated
- tenant A ordinary member
- tenant B hostile UUID
- manager
- admin/consultant
- system admin
- removed/suspended membership
- expired/revoked capability
- malformed/oversized body
- concurrent/replayed request
Run HawkScan only against local synthetic Supabase using
LOCAL_DAST_RUNBOOK.md; never point the saved authenticated scan at production.
Vulnerability Response
- contain access/provider key,
- preserve evidence without leaking data,
- rotate compromised secrets,
- fix source/migration,
- test exploit and regression locally,
- deploy through controlled path,
- verify logs/config/data exposure,
- document incident and remaining cleanup.
For service-role exposure follow docs/SECURITY_KEY_ROTATION_RUNBOOK.md.
Extension Checklist
Before shipping:
- actor and tenant relationship explicit
- service-role query cannot cross tenant
- public response non-enumerating and bounded
- provider spend controlled/accounted
- retry/concurrency semantics safe
- sensitive values absent from browser/logs
- CSP/provider data boundary reviewed
- negative persona and DAST coverage added