Document — Culprit Security Posture
Security
Culprit is built around the premise that you should be able to send us your alerts without also sending us your customer data. Here’s how that actually works in practice.
01 / 07 — Tokenization
How tokenization works
Alerts arrive at a Cloudflare Worker that authenticates the sender, encrypts the raw payload with pgp_sym_encrypt, and parks it in a vault table keyed to your tenant. The edge handler returns 200 OK and enqueues the work — no downstream system sees plaintext on the ingest path.
A detector then walks the payload for PII and ePHI — email addresses, IP addresses (v4 + v6), internal hostnames, secrets and API keys, identifiers (UUIDs, hashes, tokens), credit cards, SSNs, and any custom patterns you configure — and swaps each match for a <TOKEN_…> placeholder. The original value is encrypted into a per-tenant token dictionary; the sanitized event is what flows into correlation, LLM prompts, logs, and notifications. Rehydration is a single route — an authenticated reveal endpoint that checks tenant scope on every request before decrypting — and every reveal writes to the audit log.
For the long-form walkthrough — why the obvious fixes (strip-on-write, drop-on-detect, redact-in-prompt) all leak, and the four hard parts of building this pipeline yourself — see How to keep PII out of your alert pipeline.
02 / 07 — Data
Data handling
Four classes of data, four different retention and encryption regimes.
| Data | Location | Encryption | Retention |
|---|---|---|---|
| Raw alert payloads | raw_alerts_vault | pgp_sym_encrypt (AES-256) | Active: indefinite / Closed: 30d |
| Tokenized events | sanitized_events | At-rest (Postgres TDE) | 90 days rolling |
| Token dictionary | token_dictionary | pgp_sym_encrypt (per-tenant key) | Matches parent incident |
| Audit logs | audit_log (pgaudit) | At-rest, append-only | 7 years (SOC2/HIPAA aligned) |
03 / 07 — Isolation
Tenant isolation
Every tenant-scoped table has Row-Level Security enabled, and every policy pins reads and writes to auth.uid()’s tenant membership. The application layer never queries with the Supabase service-role key on a user-facing path — that credential is quarantined to a narrowly scoped admin wrapper that asserts an internal x-admin-verified header set by our middleware. An ESLint rule blocks direct service-role imports in the web app, so a fresh session cannot silently reintroduce a bypass. Tenant crossover is a database-level impossibility, not a convention we trust humans to follow.
04 / 07 — AI safety
AI safety and prompt injection
Root-cause analysis runs an LLM over your alert data, so a hostile payload in your pipeline could try to manipulate the model. Prompt injection is an open research problem; we do not claim to have solved it. What we have done is bound the blast radius.
The model only ever sees the tokenized event — PII has already been replaced with <TOKEN_…> placeholders before the prompt is built. The RCA call has no tool use, no function-calling, no internet access, and a hard max_tokens cap; output is constrained to a JSON schema. RAG retrieval over prior incidents is pinned to the calling tenant at the SQL predicate. Generated text is rendered as plain React children with markdown links stripped at dispatch — never via dangerouslySet-prefixed props, which an ESLint rule blocks repo-wide.
That posture makes a few outcomes structurally impossible: a poisoned alert cannot exfiltrate raw PII (the model never sees it), execute XSS against our UI (no HTML sink exists), or read another tenant’s data (the RAG predicate forecloses it). Casual ignore previous instructions jailbreaks are also reliably caught.
What the layered defense reduces but does not eliminate: a customer-internal attacker — a compromised source system, a malicious insider, or a poisoned upstream log line — can shape an RCA narrative that misleads an on-call into the wrong remediation. No LLM-bearing architecture fully closes that door. Your upstream control over what reaches /v1/ingest is the first line; on our side, thumbs-down feedback excludes a bad RCA from the RAG corpus, and every analysis call lands in an append-only audit log.
05 / 07 — Compliance
Compliance posture
Culprit’s data-handling controls — encryption-at-rest, tenant isolation by RLS, audit logging via pgaudit, documented incident response — align with SOC 2 Type II common criteria and HIPAA technical safeguards. We are not yet certified/attested. Our attestation roadmap and a BAA template are available on request for prospective customers whose compliance requirements make this material.
A HIPAA Business Associate Agreement is available for HIPAA-covered entities on request — contact legal@theculprit.ai.
06 / 07 — Vendors
Vendors and what they see
Six subprocessors touch the data path. None of them see raw incident PII.
| Vendor | What they see |
|---|---|
| Supabase | Encrypted payloads and tokenized events at rest. Postgres instance is tenant-isolated via RLS. No vendor employee holds a per-tenant decryption key. |
| Cloudflare | Inbound webhook bodies transit Workers for HMAC verification, then move to Queues as encrypted blobs. Worker logs contain only tokens. |
| Anthropic | Receives tokenized incident context for root-cause analysis. Prompts contain <TOKEN_…> placeholders only. Zero Data Retention is enabled on the API key. |
| OpenAI | Receives tokenized events for embedding. Placeholders only; raw values never leave Postgres. Inputs are not retained for training. |
| Resend | Transactional email (invite links, password resets). Recipient address and the tokenized message body — no incident PII in subject or body. |
| Paddle | Merchant-of-record billing. Receives the account-owner’s name, billing address, payment-method token, and transaction history — never sees incident PII or the tokenization keys. |
07 / 07 — Incident response
Incident response
In the event of a confirmed breach affecting customer data, we notify affected tenants within 72 hours of discovery — consistent with HIPAA breach-notification rules and GDPR-style timelines. Notifications include the scope of the incident, the data classes involved, and the remediation status. Security researchers and customers can reach the security team at: