Compliance

This page narrates Escalate's data-handling posture for customers evaluating it pre-DPA. The legal-contract version of these claims lives at /legal/privacy and /legal/dpa; this page exists to give you a plain- English read on what Escalate stores, what it does with that data, and what guarantees back each claim.

Three tiers of claim — each backed by a different defense mechanism:

  • Tier A — Code-defended. Enforced by source-code invariants with structural CI gates. If a future PR weakens the claim, the build fails.
  • Tier B — Contractually defended. Enforced by the DPA + LLM- provider sub-processor agreements. Read the DPA for the binding language.
  • Tier C — Process-defended. Enforced by an internal agent-coordination review rule (no code gate). If a future PR introduced a violating code path, founder review is the only catch. Available to customers via DPA + an annual security questionnaire on request.

§1 What Escalate stores + how

Tier A — Per-workspace data isolation

Every row in Escalate's Postgres has a workspace_id column and a Row-Level Security policy that restricts reads + writes to rows matching the requesting workspace's ID. Cross-workspace reads are structurally impossible.

Defense: A boot-time schema assertion at packages/shared/src/db/schema-assertions-runner.ts verifies the RLS policies exist + are enabled on every table before the worker or bot processes accept traffic. Boot fails fast if any policy is missing or relaxed.

Cross-link: /legal/privacy restates this in contract-grade language.

Data inventory (narrative)

What Escalate stores per workspace:

  • Slack message metadata + content for channels the bot created (deal channels only — no DMs, no arbitrary channels).
  • CRM deal records (Salesforce Opportunity / HubSpot Deal — id, name, amount, stage, last-update timestamp).
  • Role mappings (which workspace user is Legal / Security / Compliance / AE).
  • Threshold configuration (per-role business-day silences).
  • Audit chain (every alert, every action you took, every classification verdict).

What Escalate does NOT store:

  • DMs. Escalate's bot scope does NOT include groups:history or arbitrary-channel read; only channels it auto-created.
  • CRM contact PII outside deal-record fields. Names, phone numbers, custom contact fields are not pulled.
  • OAuth refresh tokens in plaintext. Stored encrypted (Supabase Vault).

§2 The LLM firewall

Escalate uses LLMs (Llama 3.3 70B as the V1 default; Anthropic Claude available) to classify sub-thread messages — is this a substantive ask, a routing question, a completed task? The classifier verdict drives what to track; it never drives how the audit chain transitions.

Tier A — Deterministic code owns state transitions

Per Persistent Decision #6 (load-bearing, can't be re-opened without explicit founder direction):

  • LLMs classify. The classifier returns a structured verdict (e.g., {kind: 'substantive_ask'} or {kind: 'routing_question'}).
  • Deterministic code owns state. The silence-tracker reads the verdict + advances state machines (threshold_state, alert_state) via plain TypeScript — no LLM in the write path.

Defense: The single-writer registry lint at tools/ci/single-writer-registry.sh enforces that every state- machine mutator in the codebase has exactly one declared writer file (plus an optional orchestrator). If a future PR added an LLM- driven write to one of these state machines, the lint hard-fails merge. The registry is at packages/shared/tests/state-writers-registry.ts.

Tier A — No cross-customer fine-tuning

Escalate's LLM access is mediated by the ChatProvider interface at packages/worker/src/classifier/providers/. This interface is the boundary at which provider-specific quirks (OpenAI- compatible vs. Anthropic SDK) get isolated from the rest of the classifier.

Equally important: this interface is also the only call site for outbound LLM traffic. Any code path that would feed customer data into a fine-tune endpoint would have to add a new method to this interface or a new file under providers/ — both visible at PR-review time.

Defense: Architecture invariants (arch:check gate) block any import of @anthropic-ai/sdk or openai from outside packages/worker/src/classifier/providers/. The boundary is structural.

§3 No-training-on-customer-data

Tier B — Contractual claim

Escalate's LLM sub-processor agreements (currently Groq + Anthropic; extensible via the ChatProvider interface) prohibit training on inference inputs.

Defense: This is a contractual guarantee with each LLM provider, NOT a code-side claim. The defense lives in the master service agreements with each provider and is restated in /legal/dpa §"LLM sub-processors."

If you need a copy of the underlying provider agreements for your own compliance review, request via the DPA process.

§4 No founder-side customer impersonation

Tier C — Process-defended

Escalate does NOT have a "log in as customer" support feature. The founder cannot impersonate a customer user to debug an issue.

Defense: Process-defended via the agent-coordination rule. The codebase has no /admin/login-as endpoint, no "set session as user X" path, no super-user session token. Debugging is done via:

  • Sentry (error reports with PII-scrubbed payloads).
  • Supabase admin reads (DB-level inspection by the founder, NOT via session-impersonation).
  • Customer screenshare (Calendly walkthrough).
  • Audit-export of the customer's own audit chain (run by the founder via /escalate audit-export workspace).

Important caveat: This is a process defense, not a structural one. There is no CI gate today that would block a future PR from adding an admin-login-as endpoint. The defense is:

  1. The agent-coordination rule (founder reviews before any PR that would add such a path).
  2. The persistent-decision protection on PD #6 (load-bearing — not re-openable without explicit founder direction).

If a customer audit asks for a structural defense (a CI lint that blocks any setSessionForUser-shaped code path), Escalate would add it. Until then, the Tier C posture is honest about its limitations.

§5 Auditing posture

Append-only audit chain (Tier A)

Every event Escalate writes to the events table is append-only. The schema enforces this:

  • No UPDATE or DELETE permitted on events rows (RLS + role GRANTs at the database level).
  • Each row has a created_at timestamp + monotonic ordering.
  • Hash-chain integrity (M7 PR D) provides per-row tamper-evidence.

Defense: The boot-time assertion verifies the RLS posture on events; a CI lint at tools/ci/event-direct-insert-guard.sh prevents direct writes that would bypass the single-writer discipline.

Audit-export (Tier A — feature surface)

You can export the full audit chain at any time via /escalate audit-export workspace (founder-only). The export includes:

  • Every alert fired + the action taken.
  • Every classifier verdict for sub-thread messages.
  • Every CRM stage transition that fired the trigger.
  • Every role-assignment + threshold change.

The export is signed (HMAC) so downstream consumers can verify integrity. See Slash commands for the export format options.

Cross-link: /legal/dpa §"Customer data export" restates this in contract-grade language.

Common questions

Where does my data physically live?

Supabase Postgres in AWS us-east-2. Single-region V1; multi- region at $500K ARR per the V1 architectural plan.

Do I have a data-deletion process?

Yes — cancellation flows through the Customer Portal trigger a 30-day read-only grace period followed by scheduled deletion. See /legal/privacy §"Deletion."

Can I bring my own LLM key?

Today: no. Escalate manages LLM access centrally via the ChatProvider interface; per-customer routing is V1.5. If you have a compliance requirement that needs your own provider, ping the founder.

Do you have a SOC 2 report?

V1: no — Escalate is pre-SOC 2 as a deliberate trade-off (founder- solo + 10-DP target makes the audit cost unjustifiable today). SOC 2 Type II is a Year-2 milestone tied to first 25-customer ARR milestone. The DPA covers the substantive controls without the certification.

Next steps