Culprit

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

PAYLOADENCRYPTTO LLMAUTHORIZEDhost:db-prod-7user:alice@acmekey: sk-9f2…pgp_sym+ rlshost:<HOST_a3f9>user:<EMAIL_4d2c>key: <KEY_e8b1>host:db-prod-7user:alice@acmekey: sk-9f2…WEBHOOK/ INGRESSVAULT/ ENCRYPTEDAI/ CORRELATIONINCIDENT/ UI
Fig 02 — Tokenization pipeline

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.

The interception work is also visible, not just claimed: the PII interception report on your Insights dashboard counts every value caught, broken down by detector type and by service — evidence you can hand to your security team, not just a promise.

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.

DataLocationEncryptionRetention
Raw alert payloadsraw_alerts_vaultpgp_sym_encrypt (AES-256)Account lifetime; per-service option to drop after tokenization
Tokenized eventssanitized_eventsEncrypted at restAccount lifetime — history powers incident-memory RCA
Token dictionarytoken_dictionarypgp_sym_encrypt (per-tenant key)Matches parent incident
Audit logsaudit_log (pgaudit)At-rest, append-only12-month minimum; retained account-lifetime today

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 is available on request for prospective customers whose compliance requirements make this material.

A HIPAA Business Associate Agreement is offered on the Enterprise tier, where the engagement funds the vendor BAA chain (database, edge, and email providers) that end-to-end HIPAA coverage requires. Self-serve workspaces must not submit ePHI (Terms §9.3). To scope an Enterprise engagement, contact legal@theculprit.ai.

06 / 07 — Vendors

Vendors and what they see

Six subprocessors touch the data path. Raw payloads are encrypted at ingest and stored only in the vault; the AI, email, and billing vendors receive tokens only.

VendorWhat they see
SupabaseEncrypted payloads and tokenized events at rest. Postgres instance is tenant-isolated via RLS. No vendor employee holds a per-tenant decryption key.
CloudflareInbound webhook bodies transit Workers for HMAC verification, then move to Queues as encrypted blobs. Worker logs contain only tokens.
AnthropicReceives tokenized incident context for root-cause analysis. Prompts contain <TOKEN_…> placeholders only. Anthropic does not train models on API inputs or outputs under its commercial terms.
OpenAIReceives tokenized events for embedding. Placeholders only; raw values never leave Postgres. Inputs are not retained for training.
ResendTransactional email (invite links, password resets). Recipient address and the tokenized message body — no incident PII in subject or body.
PaddleMerchant-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:

security@theculprit.ai

build 29c4e16updated 2026-07-03no trackersno analyticsno third-party scripts