Skip to main content

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

BoundaryTrust rule
BrowserUntrusted; anon key is public
RLS/RPCAuthoritative for direct Supabase access
Edge Function service clientBypasses RLS; must explicitly authorize
LiveKit agentService-role host; validate room/session/speaker
Gemini/Resend/LiveKit/R2/SentryExternal processors; minimize data/secrets
Public token endpointsCapability 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_admin and company_consultant are 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: DENY
  • X-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

  1. contain access/provider key,
  2. preserve evidence without leaking data,
  3. rotate compromised secrets,
  4. fix source/migration,
  5. test exploit and regression locally,
  6. deploy through controlled path,
  7. verify logs/config/data exposure,
  8. 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