Trust & Security
What we actually do
1. Tenant data isolation Proven
Client matter data is isolated at the database layer using PostgreSQL Row-Level Security (RLS). Every query runs under the requesting firm's tenant context. Without it, the query returns zero rows — not a cross-tenant spill.
Fail-closed. The original RLS policies were fail-open: a missing tenant context returned all rows. Migration 019 closed this on the conflict-check path (intakes, calls, compliance flags, audit log, and matters). Migration 021 closed it on telemetry tables (events, agent sessions). The absence of a tenant context is no longer a see-all signal — it returns zero rows for tenant-scoped data.
Live two-tenant proof. A live Postgres verification ran against two real tenants: Firm A's call notes returned zero rows under Firm B's tenant context. That proof is wired into the test suite and re-runs against the real database.
- Tables confirmed fail-closed:
intakes,calls,compliance_flags,matters,audit_log,events,agent_sessions - Admin access is an explicit opt-in via the
app.is_admindatabase flag — not an implicit consequence of missing tenant context - The
audit_log,events, andagent_sessionstables preserve a carve-out for system-level rows with no tenant (NULL tenant_id); those system rows remain visible to the admin role only, not to any law firm tenant
What is not yet covered. Approximately 15 additional tables in the schema remain fail-open; fail-closed RLS for those is in progress. We note this explicitly rather than claiming universal RLS coverage.
2. Encryption at rest Proven (for PII fields)
Caller PII — name, phone number, email address, and intake notes — is encrypted at the field level before storage. The implementation uses Fernet authenticated encryption (AES-128-CBC + HMAC-SHA256), in foundation/pii_crypto.py.
Per-tenant key derivation. Each firm's encrypted fields are protected by a key derived from a master key via HKDF-SHA256, using the tenant identifier as context. A compromise of one tenant's derived key does not expose another tenant's data.
What is not covered. Call recording URLs are stored as plaintext references to the VAPI recording service — they are not re-encrypted at the FirmEdge application layer. Legacy flat-file stores (caller_memory.json, call_logs.json) are also unencrypted. We are explicit about this rather than claiming universal encryption at rest.
3. Conflict-of-interest screening Proven
When a caller contacts a firm through FirmEdge, Alex runs a conflict-of-interest screen before booking a consultation. The screen compares caller name and matter description against the firm's client and matter list using a fuzzy name-match algorithm (≥ 85% similarity threshold, foundation/conflict_checker.py).
Fail-closed. If the conflict screen cannot complete — Clio lookup failure, database error, or any exception — booking is paused, not permitted. The caller is told only that the appointment cannot proceed now; the reason is never disclosed to the caller.
Server-side booking interlock (PR #519). A booking is only allowed if a clean screen result was recorded by the server for that specific call. There is no client-side path to clear the gate — replaying a booking request without a recorded clean screen result will not succeed.
Limitations — this is an aid, not a legal determination. The fuzzy match can miss entity names, DBAs, nicknames, name changes, and adverse-party relationships. It escalates potential conflicts to the attorney; it does not replace the firm's professional conflict screening obligation under applicable bar rules.
4. AI disclosure & attorney oversight Proven (prompt-enforced)
Alex is designed to identify itself as an AI at the start of each call and announce that the call may be recorded. This applies on every call regardless of the caller's state, including two-party-consent states where recording disclosure is legally required.
Honest about what "instructed" means. These disclosures are rules in Alex's system prompt, not hard-coded deterministic controls. Like any LLM instruction, Alex could paraphrase or under rare circumstances skip them. We describe this as "designed and instructed to" rather than "on every call without exception" — the DPA reflects the same language.
Attorney-in-the-loop — enforced at the application layer. Alex is instructed not to give legal advice, quote prices, or send any document without attorney approval. Every engagement letter, intake packet, and client email waits in the firm's review queue. The "drafts, never sends" model is enforced in application code — approval is required server-side before any document is released, not just as an LLM instruction.
Post-call UPL detection. After each call, a classifier reviews the transcript for unauthorized practice of law markers and flags potential issues to the attorney. This is a detection aid, not a real-time speech filter.
5. Outbound send controls Proven
All outbound messages (email, SMS) pass through a single send-guard chokepoint (send_guard.py) with multiple protection layers:
- Kill switch. A file-based kill switch halts autonomous outbound cold outreach instantly, without a code change or deploy.
- Anomaly detection. Automated spike detection monitors send volume; anomalous patterns trigger an automatic kill and alert.
- Daily and per-category caps. Global daily send limits plus per-message-category rate limits prevent runaway sends.
- Fail-closed. If the guard cannot evaluate safely — file read failure, check error — it defaults to blocking the send.
6. Client opt-out / unsubscribe In progress
Clients contacted by a firm through FirmEdge can opt out of further outreach. FirmEdge has built an opaque, HMAC-authenticated token mechanism for unsubscribe URLs (/u/<token>): the token encodes the tenant and recipient without exposing either in the URL and cannot be guessed or enumerated.
Once a recipient opts out, they are added to a tenant-scoped suppression list checked by the send guard on every subsequent send. The check is at the send chokepoint — individual automation workflows cannot bypass it.
What is not yet confirmed. The opaque token mechanism is built and the server-side route is registered; we have not independently verified that the /u/<token> link is included in every live outbound email and SMS footer. CAN-SPAM requires a working opt-out link in every commercial message. We are marking this "In progress" rather than "Proven" until that end-to-end wiring is confirmed.
7. Data deletion & retention Proven (deletion); retention purge is dormant pending activation
Firms can request deletion of their data. FirmEdge's deletion processor NULLs PII fields on calls and intakes scoped to the requesting tenant, with RLS ensuring the operation only affects that firm's rows. The processor is in deletion.py.
Flat-file store limitation. Legacy flat-file stores (caller_memory.json, call_logs.json) are not reached by the deletion processor. A verified deletion request does not currently extend to those files. We note this explicitly.
A 90-day automated retention purge is built (migration 023, retention.py), including per-tenant configurable retention windows and legal-hold flags. The purge runs in dry-run mode by default and is not currently active in production. It becomes operational when RETENTION_PURGE_ENABLED=true is set in production.
8. Sub-processors
The following third-party services receive client and caller data as part of delivering FirmEdge. This list was verified against the actual codebase — services not wired in production code are excluded. The operative sub-processor terms are in the Data Processing Agreement (§6).
| Sub-processor | Purpose | Data received |
|---|---|---|
| VAPI | AI phone call infrastructure | Call audio, transcripts, caller phone number |
| Anthropic | AI language model (powers Alex) | Call transcripts and intake facts in system prompts |
| Stripe | Subscription billing | Billing details (FirmEdge never receives raw card numbers) |
| Clio | Practice management sync | Client and matter data — only when the firm connects Clio via OAuth |
| SendGrid | Transactional & outreach email | Recipient email address and message content |
| Twilio | SMS delivery | Recipient phone number and SMS content |
| OAuth / Gmail send / Analytics | OAuth tokens for the firm's Gmail; analytics events on the marketing site (consent-gated, no client matter data) | |
| Cloudflare | Bot detection (Turnstile) at signup | Browser signals at the signup form — no client matter data |
Note: Apify and Telegram are used in FirmEdge's own internal workflows (prospecting alerts); they do not receive a law firm's client data. SignWell (e-signature) is not integrated and does not appear above. Calendly appears as a default scheduling link in some outbound templates but is not a per-firm integration — no client appointment data flows to Calendly through FirmEdge. We will update this page before activating any new sub-processor that receives client data.
9. How we build and test security
Adversarial red-team review
Before merging security-critical changes, we run independent adversarial review passes. The conflict-screening interlock, RLS fail-closed migrations, auth hardening, and PII encryption layers each went through a pre-merge adversarial pass that surfaced and closed findings before landing. The discipline is per-PR, recorded in each commit, not ad hoc.
Forward-only migration runner
Database schema changes run through a tracked, forward-only runner that prevents accidental re-application of migrations in production. Each migration is atomic and logged.
Constant-time authentication
Auth comparisons — password hashes, reset tokens, HMAC signatures — use constant-time equality to prevent timing oracle attacks throughout the application.
PII out of application logs
Caller name, phone, and email are not written to application logs. The event log uses HMAC-hashed identifiers at ingest. Debug logs in the AI receptionist layer mask caller identity fields.
No-secrets pre-commit hook
A local pre-commit hook blocks commits that contain credential patterns. Every CODEOWNERS-listed path (security primitives, auth, migrations, agents) requires an adversarial review marker in the commit message as a discipline gate. Bypasses are logged in an incident ledger.
10. What we are building toward
The following are on our security roadmap. They are explicitly labeled On roadmap — not current capabilities.
- SOC 2 Type II audit On roadmap — no current certification
- Independent formal penetration test On roadmap — we run internal adversarial review today; no third-party pen-test has been conducted
- Sub-processor change notification channel On roadmap — the DPA commits to 14-day notice of material sub-processor changes; the notification mechanism is not yet operational
- BAA (Business Associate Agreement) On roadmap — FirmEdge v1 is not designed for HIPAA-covered entities; a BAA is not currently offered
- Fail-closed RLS for all schema tables In progress — core matter, conflict, and telemetry tables are fail-closed; ~15 additional tables are pending
- Application-layer re-encryption of call recording URLs On roadmap — recording URLs are currently stored as plaintext references to the VAPI endpoint
11. Questions and incident reporting
Security questions, data access requests, deletion requests, or suspected incidents: hello@firmedge.io.
To review a full copy of our Data Processing Agreement or discuss specific security requirements for your firm, contact us at the same address.