Legal

Security overview

This page lists the security controls implemented in ELAYGENT today. Each control names the product area that enforces it so procurement and security reviewers can verify the claim.

We do not claim certifications we do not hold. The "What we do NOT claim" section at the bottom of this page is part of the honest answer, not a footnote.

Access control

Role-separated access (Platform admin / Practice admin / Staff)

Implemented

Three distinct roles enforced at every protected route. Staff cannot reach administrator surfaces; practice administrators cannot reach cross-tenant platform surfaces. Recovery actions on the operator dashboard are also role-split (staff can mark contacted; administrators can mark converted or lost).

Limits

  • Custom roles beyond these three are not supported today.

Per-request session and disabled-user revocation

Implemented

Every protected request re-fetches the user's `users.status`. A user marked `disabled` is signed out on the next request and redirected to `/login?reason=disabled`. Disable also bans the Supabase auth user via admin API so refresh tokens stop working.

Limits

  • Window between disable and revocation is bounded by the next request from the affected user — not instantaneous on already-rendered pages.

Tenant isolation via Supabase Row-Level Security (RLS)

Implemented

Tenant-scoped tables have RLS policies enabled in Supabase migrations. The portal additionally re-asserts tenant scope in every server-side query (defense-in-depth). The audit log query in the platform admin console Audit and the operator dashboard audit view filters by tenant identifier explicitly.

Limits

  • Platform admin role intentionally bypasses tenant scoping for support workflows. Platform admin actions are audited.

Data protection

Server-side proxy for all backend tenant data

Implemented

Tenant API keys, AI provider keys, Vapi credentials, Twilio credentials, and Supabase service-role keys never reach the browser. The portal proxies backend reads through a server-side proxy which injects the tenant key server-side.

Limits

  • Browser does receive operational payloads (call summaries, lead text, dashboard metrics) — those are tenant-scoped, not secret.

AI provider keys encrypted at rest

Implemented

When a tenant configures their own AI provider key (BYOK), it is encrypted with a server-managed encryption secret before being stored. Decryption happens server-side at request time only, and new ciphertexts are cryptographically bound to the tenant/provider context.

Limits

  • Encryption key is environment-managed by ELAYGENT — not customer-managed (CMEK is not supported).

Mobile-surface financial-field scrubbing for staff role

Implemented

Revenue estimates (at-risk, won, leakage, per-call/per-lead dollar figures) are stripped from server-rendered HTML for tenant-staff users on the mobile surface, in addition to UI-level masking. Admins see full financials.

Limits

  • Mobile-surface only. Desktop routes return full payloads to admins as before — this is mobile minimization, not a tenant-wide access control.

Encryption in transit

Implemented

All hosted endpoints (portal, backend, Supabase, Vapi, Twilio, Stripe, OpenAI, Anthropic) are HTTPS-only. PMS bridge URLs are validated to be HTTPS before any request is signed and dispatched.

Limits

  • Customer-hosted PMS bridges that are HTTP-only are rejected at request time, not silently downgraded.

Audit visibility

Typed audit log with enumerated actions

Implemented

Privileged actions write to a shared audit log table with a typed enumerated action set enum. Practice administrators and platform admins can read filtered audit views from the platform admin console Audit (desktop, full) and the operator dashboard audit view (last 100 mobile + agent-config rows).

Limits

  • CSV and JSON export are available from the desktop Audit view for the filtered result set.
  • Retention policy is documented in docs/AUDIT-RETENTION-POLICY.md. Automated retention enforcement is config-gated by an admin-managed retention flag and uses one platform-wide retention window, not per-tenant retention settings.

Platform admin tenant portal-data export

Implemented (with documented limit)

Platform admins can export portal-owned Supabase data for a tenant as JSON from `/api/admin/tenant-export?tenant identifier=<uuid>` or the the platform admin console All Tenants export link. The export is scoped to one tenant and omits provider secrets.

Limits

  • This is portal-owned Supabase data only. Backend call records, Vapi recordings, Twilio logs, Stripe-hosted payment records, PMS data, and other subprocessor-held records are separate systems.
  • Tenant hard deletion is separate from export. The shipped hard-delete route is platform admin-only, portal-owned data only, legal-hold blocked, and subprocessor-erasure-status gated.

Platform admin tenant deletion-preparation suspension

Implemented (with documented limit)

Platform admins can suspend a tenant before deletion after confirming the tenant export was taken and presenting the destructive-operations secret. The workflow disables the tenant, revokes affected Supabase auth sessions, and writes a deprovision audit entry.

Limits

  • This suspends access before deletion; it does not hard-delete portal rows.
  • It does not erase backend call records, Supabase Storage files, Stripe records, Twilio logs, Vapi recordings, PMS data, or other subprocessor-held data.
  • Legal hold and subprocessor erasure state now gate the hard-delete route, but automated provider erasure is not shipped.

Gated portal-owned tenant hard delete

Implemented (with documented limit)

A platform admin-only hard-delete route deletes portal-owned Supabase rows and safe KB Storage objects in dependency order after destructive-secret verification, deletion preparation, export confirmation, legal-hold check, and provider erasure-status evidence.

Limits

  • This is not one-click deletion and is not self-serve in the customer UI.
  • It deletes portal-owned Supabase rows and KB Storage objects only.
  • It does not delete backend Prisma records, Vapi recordings, Twilio logs, Stripe records, PMS data, or other subprocessor-held data.
  • Subprocessor erasure rows are evidence gates only; no provider deletion adapters are shipped.

Abuse resistance

Rate limiting on auth-adjacent and proxy endpoints

Implemented (with documented limit)

Sliding-window rate limiter with configurable window and max-per-identifier. Used on auth endpoints and tenant-scoped proxy routes.

Limits

  • Production rate limiting uses Upstash Redis and fails closed when Upstash is missing or unavailable. Local development falls back to an in-memory Map, which is not a production control.
  • Coverage is documented in docs/RATE-LIMIT-COVERAGE.md. Not every authenticated read route has a custom limiter yet.

Webhook signature verification on inbound provider events

Implemented

Billing, PMS bridge, and voice-provider webhooks verify provider-specific secrets or HMAC signatures before processing.

Limits

  • Customer-hosted bridges are responsible for verifying signatures on their side using the same shared secret.

Operational

Legal hold and subprocessor erasure runbooks

Implemented (with documented limit)

Operator runbooks now define how to suspend deletion for legal hold and how to request erasure from subprocessors after tenant deletion preparation. The database has a legal-hold flag per tenant and a per-tenant erasure-status record, and the hard-delete route refuses held tenants or pending provider statuses.

Limits

  • These are still manual operational runbooks, not automated provider erasure orchestration.
  • Admin UI for legal-hold history and erasure evidence management is not shipped yet.
  • No provider-specific erasure deletion adapters are shipped yet.
  • Audit retention does not yet have tenant-scoped legal-hold exclusion logic.

Cron endpoint authentication

Implemented

Cron routes under scheduled job require a dedicated scheduler Bearer token. Backend cron routes no longer accept admin API secrets as scheduler credentials.

Limits

  • If the scheduler secret is unset, cron routes fail closed.
  • Audit-log retention deletes also require a separate retention secret when retention enforcement is enabled.

Operational alerting on cron failure

Implemented

Cron job failures can emit a notification to a configured Slack/Discord-compatible alert webhook. When unset, failures still log server-side but no remote alert fires.

Limits

  • Alerting is best-effort and depends on the configured webhook being reachable.

Optional Sentry monitoring with PII scrubbing

Implemented (with documented limit)

Sentry initializes only when DSN environment variables are set. Portal and backend Sentry configs set default PII collection disabled and route events through shared scrubbers that redact obvious secrets, auth headers, cookies, emails, phone numbers, patient/caller/transcript fields, and raw request bodies before events are sent.

Limits

  • Scrubbing reduces obvious leakage risk but is not a guarantee that no PHI can ever reach Sentry.
  • Sentry remains optional and should be enabled for healthcare tenants only after Sentry project settings and BAA/DPA posture are reviewed.

What we do NOT claim

The following are intentionally not implemented or not certified today. We surface them here so a buyer can make an informed decision rather than discovering a gap during an audit.

Diligence pack

Procurement, security, and compliance reviewers can request implementation references, configuration evidence, subprocessor BAA details, and the latest security review summary. Email security@elaygent.com from a corporate domain and we will share the pack under NDA.