Security, GDPR & Compliance: How We Protect Every Byte
Turnstile, Content Security Policy, OAuth PKCE, per-tenant isolation, cookie consent, right to erasure and premium feature guards — Cadences' multi-layer security explained piece by piece.
Gonzalo Monzón
· Founder & CTO
Security isn't a feature you add later. In Cadences, it's an architectural decision that affects every layer of the stack: from how each organization's data is isolated to how a click on a registration button is validated. This article explains the complete security model.
🛡️ Defense in Depth Model
Cadences implements 7 independent security layers. If one fails, the rest keep protecting. There's no single point of failure in the security chain.
Multi-Layer Security Map
Every request reaching Cadences passes through multiple verification layers before touching a single piece of data:
| Layer | Mechanism | Protection |
|---|---|---|
| 1. Edge | Cloudflare WAF + DDoS | Volumetric attacks, HTTP-level SQL injection |
| 2. Bot Protection | Turnstile (invisible) | Bots, scraping, fake registrations |
| 3. Authentication | OAuth 2.0 + PKCE + JWT | Unauthorized access, session hijacking |
| 4. Authorization | RBAC + Feature Guards | Privilege escalation, premium feature access |
| 5. Isolation | D1 per tenant + V8 isolates | Cross-tenant data leak |
| 6. Transport | TLS 1.3 + HSTS + CSP | MITM, XSS, clickjacking |
| 7. Secrets | Cloudflare Secrets + env vars | API key exposure, token leaks |
Turnstile: Invisible Anti-Bot Protection
Cadences uses Cloudflare Turnstile instead of traditional CAPTCHAs. The difference is critical: Turnstile verifies the user is human without interrupting the experience. No puzzles, no "select all traffic lights".
Traditional CAPTCHA
- ❌ Interrupts user flow
- ❌ Poor accessibility
- ❌ Google tracks the user
- ❌ 8-15% abandonment rate
Cloudflare Turnstile
- ✅ Invisible — zero friction
- ✅ Accessible by default
- ✅ No third-party tracking
- ✅ ~0% abandonment rate
async function verifyTurnstile(token: string, ip: string) {
const response = await fetch(
'https://challenges.cloudflare.com/turnstile/v0/siteverify',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
secret: env.TURNSTILE_SECRET_KEY,
response: token,
remoteip: ip,
}),
}
);
const result = await response.json();
if (!result.success) {
throw new Error('Bot detected — request rejected');
}
return result;
} Turnstile is applied on all critical forms: registration, login, contact and payments. The token is verified server-side in the Worker, never on the client.
Authentication: OAuth 2.0 + PKCE + JWT
Cadences supports two distinct authentication flows depending on context:
🌐 Web App (GIS Flow)
- 1. Redirect to Google OAuth
- 2. Google returns auth code
- 3. Worker validates code + client_secret
- 4. JWT signed with HMAC-SHA256 is generated
- 5. JWT includes orgId + userId + role + exp
🖥️ Desktop (PKCE Flow)
- 1. Generate code_verifier + code_challenge
- 2. Open browser with challenge
- 3. Localhost callback receives code
- 4. Exchange code + verifier for tokens
- 5. No client_secret on the device
⚠️ Why PKCE for Desktop?
Desktop apps cannot securely store a client_secret — any user can decompile the binary. PKCE (Proof Key for Code Exchange) eliminates that need: the cryptographic challenge replaces the secret, and only the process that started the flow can complete it.
Premium Feature Guards
Not all users have access to the same features. Cadences implements a feature guard system that protects costly APIs and premium functionality:
class PremiumAPIAccessError extends Error {
constructor(feature: string) {
super(`Premium feature "${feature}" not available`);
this.name = 'PremiumAPIAccessError';
}
}
function requirePremiumFeature(org: Org, feature: string) {
const allowed = org.plan?.features || [];
if (!allowed.includes(feature)) {
throw new PremiumAPIAccessError(feature);
}
}
// Usage example in Voice AI endpoint
requirePremiumFeature(org, 'voice_calls');
requirePremiumFeature(org, 'elevenlabs_tts'); This is especially important for pay-per-use APIs (ElevenLabs, Twilio, OpenAI). A free plan user cannot generate voice calls no matter how well they know the endpoint URL.
Free Plan
Basic CRM, 1 storefront, limited AI agents, no voice, no TTS
Pro Plan
Voice calls, TTS, unlimited agents, multiple storefronts, webhooks
Enterprise Plan
SSO, API access, custom agents, dedicated support, 99.9% SLA
Total Data Isolation
Data isolation in Cadences isn't a software filter — it's an architectural guarantee. Each organization has its own D1 database (SQLite at the edge). There's no WHERE org_id = ? — the entire database belongs to a single tenant.
Data Isolation
Physical impossibility of accessing another tenant's data. No cross-tenant queries because there are no shared tables.
Compute Isolation
Each request executes in an independent V8 isolate with its own CPU and memory limits.
Storage Isolation
Files in R2 under per-organization prefix. KV namespaces with org-scoped keys. No collision possible.
Auth Isolation
JWTs signed per-org with HMAC-SHA256. A token from org A never works against org B.
Content Security Policy & Headers
Every HTTP response from Cadences includes security headers that block attacks before the browser executes malicious code:
// Content Security Policy — controls what the browser can load
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline' challenges.cloudflare.com;
style-src 'self' 'unsafe-inline' fonts.googleapis.com;
img-src 'self' data: images.unsplash.com *.googleusercontent.com;
connect-src 'self' *.cadences.app api.elevenlabs.io;
frame-src challenges.cloudflare.com;
// Other security headers
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(self), geolocation=() | Header | What It Prevents |
|---|---|
| Content-Security-Policy | XSS, script injection, data exfiltration |
| Strict-Transport-Security | Downgrade attacks, HTTP MITM |
| X-Frame-Options: DENY | Clickjacking via iframes |
| X-Content-Type-Options | MIME sniffing attacks |
| Permissions-Policy | Unauthorized camera, mic, GPS access |
In Electron apps (CadencesLab, Audio Hub), context isolation is also applied: the app code has no direct access to Node.js APIs. All communication goes through a preload bridge with typed IPC.
GDPR: Real Compliance, Not Just a Banner
GDPR compliance isn't achieved with a cookie banner. It's achieved with architectural decisions that enable exercising every right the regulation establishes:
📋 Right of Access (Art. 15)
Complete user data export in JSON/CSV format. A single button in Synapse Studio generates the full package: contacts, deals, activities, files.
✏️ Right to Rectification (Art. 16)
All data is editable by the owning user. No locked fields, no opaque processes. Edit, save, done.
🗑️ Right to Erasure (Art. 17)
This is where the database-per-tenant architecture shines. Want to delete all data for an organization?
// GDPR Article 17: Right to erasure
await env.CF_API.deleteD1Database(org.dbId);
await env.R2.deletePrefix(`org/${orgId}/`);
await env.KV.deletePrefix(`org:${orgId}:`);
// The entire organization vanishes. No remnants. No soft-delete. No flags. DROP DATABASE. Total isolation means total deletion.
📦 Right to Data Portability (Art. 20)
Export in standard formats (JSON, CSV) of all user data. Compatible with import into other CRMs.
🍪 Cookie Consent (Art. 7)
Consent banner on every Storefront. No tracking until the user explicitly accepts. Google Analytics and third-party scripts blocked by default.
Secrets Management
Cadences manages dozens of API keys and secrets from external services: Google OAuth, Twilio, ElevenLabs, Stripe, DeepSeek, OpenAI, Anthropic. None of these secrets appear in the source code:
✓ How They're Stored
- • Cloudflare Secrets (encrypted at rest)
- • Environment variables in wrangler.toml (dev)
- • Per-environment secrets (staging/production)
- • Periodic OAuth token rotation
✕ Never Done
- • Hardcoded secrets in source code
- • Secrets in localStorage or cookies
- • API keys in URLs or query params
- • Logs with sensitive data
Rate Limiting with Durable Objects
Each organization has its own rate limiting Durable Object. We don't share counters across tenants. This means:
| Endpoint | Limit | Window |
|---|---|---|
| General API | 1,000 req | per minute / org |
| Auth (login/register) | 10 req | per minute / IP |
| ElevenLabs TTS | 50 req | per hour / org (Pro plan) |
| Voice Calls (Twilio) | 20 calls | per hour / org |
| AI Agents | 100 exec | per hour / org |
| Incoming Webhooks | 500 req | per hour / org |
The rate limiter uses a sliding window implemented with Durable Objects. Each org has its own DO, meaning abuse from org A doesn't affect org B (no noisy neighbor problem).
Complete Security Checklist
🔐 Authentication
- ✅ OAuth 2.0 with Google
- ✅ PKCE for desktop apps
- ✅ JWT with configurable expiration
- ✅ Encrypted refresh tokens
- ✅ Server-side session invalidation
🛡️ Infrastructure
- ✅ Cloudflare WAF + DDoS protection
- ✅ TLS 1.3 everywhere
- ✅ HSTS with preload
- ✅ Zero trust networking
- ✅ Edge-native (no own servers)
📊 Data
- ✅ Database per tenant
- ✅ Encryption at rest (D1)
- ✅ Automatic daily backups
- ✅ GDPR data export
- ✅ Right to erasure (DROP DB)
🖥️ Desktop Apps
- ✅ Context isolation (Electron)
- ✅ Typed IPC bridge
- ✅ No nodeIntegration
- ✅ CSP in renderer
- ✅ Signed auto-update
Is Your Platform GDPR Compliant?
If you're building a SaaS and need to comply with privacy regulations, Cadences already does it for you. Every organization operating on Cadences automatically inherits the complete security model.
Start with CadencesConclusion
Security in Cadences isn't a checkbox — it's an emergent property of the architecture. Per-tenant isolation isn't a SQL filter, it's a separate database. Anti-bot protection isn't an annoying CAPTCHA, it's invisible Turnstile. GDPR compliance isn't a cookie banner, it's the ability to DROP DATABASE and have everything disappear.
Each layer works independently. If a vulnerability is discovered in Turnstile tomorrow, the other 6 layers keep protecting. If a JWT is compromised, the rate limiter and feature guards still block unauthorized access. Defense in depth isn't a buzzword — it's the only serious way to build software in 2026.
The best security is the kind users don't notice but attackers can't overcome.
Gonzalo Monzón
Founder & CTO at Cadences