Security
Posture, controls, threat model, and disclosure process.
Posture summary
| Control | Implementation |
|---|---|
| Transport | TLS 1.3 everywhere |
| Key storage | Peppered SHA-256; pepper in Cloudflare Secrets Store, never in D1 or logs |
| Key format | ck_live_<26-char Crockford base32>, 130-bit entropy |
| Auth timing | 80 ms uniform floor across all outcomes |
| Auth envelope | Uniform UNAUTHORIZED; real reason in audit_log only |
| Rate limit (L1) | Per-IP, Cloudflare Workers Rate Limiting, 60 / 60s, fails closed |
| Rate limit (L2) | Per-key, D1 sliding window, 1000 / 60s, keyed on 8-char prefix |
| CORS | Per-org allowed_origins + env-level global allowlist for preflight |
| Body caps | 1 MB single / 5 MB batch / 100 interactions per batch |
| Retention | interactions 24 months, rate_limits 2 hours, audit_log + alerts 90 days |
| Tenancy isolation | Every row scoped to org_id; router-enforced, not just query-enforced |
| Supply chain | SBOM generated per release (CycloneDX); gitleaks pre-commit |
Data at rest
- D1: Cloudflare-managed encryption at rest (AES-256).
- Secrets Store: Cloudflare-managed KMS.
Data in transit
- Client to edge: TLS 1.3.
- Edge to D1: Cloudflare-internal encrypted fabric.
Threat model highlights
- Cross-tenant reads: Blocked at the router. Every handler derives
org_idfrom the authenticated key, never from a request parameter. - Timing side channels on auth: 80 ms uniform floor across unknown prefix, wrong hash, inactive, expired, rate-limited.
- Enumeration of keys: Not possible; keys are not stored plaintext post-cutover; prefix is non-secret by design.
- Injection into prompts (MCP preview): Arguments sanitized against host-reserved control tokens.
OWASP API Top 10 (2023) mapping
Full mapping in the repo at docs/trust/owasp-api-top-10.md. Summary: all 10 categories are either controlled-for or not applicable to the API surface.
Responsible disclosure
[email protected]. Triage within 48 hours, critical fix within 14 days. Safe-harbor clause for good-faith research. See SECURITY.md in the repo for scope and out-of-scope categories.