Security
ClickStream is built on the principle that operators should be able to verify our security posture without taking our word for it. Every sensitive pathway — raw-PII reveal, API key rotation, admin actions — is logged, rate-limited, and scoped to a specific operator identity.
This page covers the production security posture. For day-to-day privacy controls (consent sources, data residency), see Privacy & compliance.
Encryption at rest
Every field containing raw PII is AES-256-GCM encrypted before it touches persistent storage, under a unique symmetric key per customer site. Per-site keys are stored in ClickStream's configuration store and used in-memory at the edge during encrypt operations; raw values are never persisted unencrypted.
Encrypted surfaces:
- Email addresses sent to the collector for server-side hashing (the SDK hashes first; the collector encrypts the pre-hash ciphertext for audit-recoverability only).
- Phone numbers same pattern.
- IP addresses — raw IP is encrypted alongside a SHA-256 hash (for household grouping on the hashed value, reveal only via the gate).
- Form-submission values — universal form-fill capture encrypts name→value pairs + file metadata + text excerpts.
The encrypted blobs live in Dashboard D1 (visitor_encrypted_fields, form_submissions). The SHA-256 hashes live in Analytics Engine (blob11 / blob12 / blob20 per the event schema).
Per-tenant HMAC isolation
Hashed identity signals in Analytics Engine are HMAC'd with a per-tenant key before storage. That means:
- Two tenants can't correlate the same visitor across their datasets even if they know each other's raw hashes.
- A compromised HMAC key affects exactly one tenant.
- HMAC rotation does not break existing analytics because the schema carries both
hmacHem(current) andhemMd5(public) in separate fields.
Raw-value reveal — the /decrypt gate
Operators with decrypt:read permission can reveal raw encrypted values on a per-visitor basis via the dashboard. Every reveal passes four independent checks:
- Authentication — valid dashboard session.
- Permission — operator's role includes
decrypt:read. - Password re-authentication — operator has re-entered their password within the last 5 minutes (
/api/auth/decrypt-unlock). Session auth alone isn't enough; re-auth is required for every sensitive action. - Rate limit — 10 reveals per operator per 15-minute window.
- Audit — an
audit_logrow is written with operator id, IP, visitor id, field type, timestamp, and the required free-text reason ("suspected fraud", "user support ticket #1234", …).
The audit log is append-only. Audit rows are retained 7 years (SOC2 window); each site's Activity tab shows recent actions, and full history is available on request through support.
API key lifecycle
- Storage — keys live in Cloudflare KV under
apikey:{key}. The collector hot-cachesClientConfigwith a 2-minute LRU for performance (~99.5% hit rate in production). - Reserved scope labels —
events:write,events:read,live:read,identity:resolve,signals:read— see API keys + auth. - Rotation — request a new key through support → deploy it to your site config → wait 7 days → support revokes the old key. The 2-minute hot cache + 30-second negative cache means rotation takes effect globally within ~2 minutes after the KV write.
- Revocation — support-assisted today. The key is deleted from KV and cached negative-lookup entries flush within 30 seconds.
Keys are opaque bearer credentials. A compromised key can be used until revoked — same trust model as Stripe restricted keys. Treat them accordingly: CI secret managers, per-environment keys, and the cs_test_ prefix for non-production use.
Admin surface — X-Admin-Key
A handful of administrative endpoints (custom hostname provisioning, KV bootstrap, force-refresh cache) require the ADMIN_API_KEY secret via X-Admin-Key header instead of the standard API key. The admin secret is set via wrangler secret put ADMIN_API_KEY. It's not issued to customers — the dashboard calls these endpoints on your behalf when you, say, provision a new tracking domain.
Rate limiting
The collector enforces per-API-key rate limits with a sliding-window counter backed by a Cloudflare Durable Object (SOC2-friendly "authoritative source"). Default caps per tier:
| Tier | Collector requests / sec |
|---|---|
| Hobby | 100 |
| Growth | 1,000 |
| Scale | 5,000 |
| Network | 25,000 |
| Enterprise | 100,000 |
Burst allowance is 2× the sustained rate for short spikes. See Rate limits for the response headers + overage behavior.
TLS / transport
- TLS 1.3 required on all customer endpoints. TLS 1.2 accepted for older browsers but not recommended. TLS 1.1 and below rejected.
- HSTS with
max-age=31536000; includeSubDomains; preloadon every response. - Cipher suites — the Cloudflare edge negotiates; we default to modern suites only.
cf.botManagement.ja3Hash+ja4are captured server-side and used in traffic classification.
Content Security Policy
The collector returns CSP headers on every response (content-security-policy: default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'). The SDK loader + bundle are served from your first-party domain, so the customer's own CSP can allow-list t.yourdomain.com without needing a third-party domain exception. Per-site CSP overrides are configurable on the Site → Security tab.
Audit log — what's captured
Every action of consequence on the dashboard writes to audit_log:
- Sign-in / sign-out (success + failure).
- Password change / reset.
- API key create / rotate / revoke.
- Site create / delete / rename.
- Tracking domain add / verify / remove.
- Operator invite / remove / role change.
/decryptreveals (plus the reason text).- Data export / delete.
- Consent-profile changes.
Retention: 7 years, append-only. Recent actions are visible on each site's Activity tab; full history is available on request through support.
Incident response
- Security issue reporting —
security@clickstream.com. Security reports are acknowledged promptly. Disclosure coordination follows the 90-day industry standard unless actively exploited in the wild. - Coordinated disclosure — report security issues to
security@clickstream.com; we coordinate disclosure case by case.
Assurance
- SOC2 controls — access control, audit logging, encryption, key rotation, change management, and vendor review controls are tracked internally.
- Security review packet — control evidence and architecture materials are shared under NDA through
security@clickstream.com.
Data residency
Covered on the Privacy & compliance page. Cloudflare's data-at-rest regions are enforced via tenant-specific wrangler.toml on Enterprise contracts; all other tiers inherit Cloudflare's default global distribution.
See also
- Privacy & compliance — consent + GDPR / CCPA / HIPAA modes
- API keys + auth — key rotation + reserved scope labels runbook
- Optional enrichment — what gets hashed, when enrichment runs, and how reveal controls work
security@clickstream.com— reach out for security review, architecture evidence, or incident reportinglegal@clickstream.com— reach out for DPA, MSA, BAA, sub-processor, or residency questions