Security
We don't see your traffic. We can't read your keys.
Driftstack's security posture starts from a single principle: Driftstack should never have a copy of anything that would let us impersonate you, intercept your traffic, or replay your sessions. The architecture below enforces that — not as policy, as code.
This page is security in plain terms. The deep technical posture — parameter by parameter, control by control — lives at /trust/security-overview.
The posture
Six promises, built into the architecture.
01 · Transport
Everything between you and us travels encrypted.
TLS 1.2 + 1.3, HSTS preload-eligible (browsers forced onto HTTPS).
All traffic to us is HTTPS — encrypted the whole way.
Cloudflare, our edge network, handles the encryption first
(terminating TLS at the edge) and strictly verifies it is
really talking to our own server at Hetzner (full "strict"
origin validation) before passing anything on. The API
server speaks TLS 1.2 / 1.3 only and sets a 2-year HSTS
header with includeSubDomains + preload
— an instruction that makes browsers refuse to ever connect
to us unencrypted. No unencrypted page (plaintext HTTP)
exists on any customer-facing path. You identify yourself by
sending your API key along with each request (a "Bearer"
key) — no client certificates to install.
02 · Egress
Sessions leave through the connection you choose.
Customer-configurable egress, per profile.
Each profile can attach its own egress configuration — its own exit to the internet (egress): a SOCKS5 proxy — including the video-call and modern-web traffic types (UDP / WebRTC / QUIC) that many proxies drop — an OpenVPN file (.ovpn), or a WireGuard file (.conf). A session bound to the profile connects to your proxy or VPN ("dials the tunnel") before the browser even launches, and address-lookup traffic can't slip out around it (DNS leaks blocked). Without an attached config, session traffic exits via Driftstack-managed infrastructure. We never store destination response bodies — the pages your sessions visit. See /trust/security-overview for the full posture.
03 · API keys
We couldn't read your keys if we tried.
Stored only as one-way scrypt hashes — never readable.
API keys are scrambled one-way with scrypt (logN=15) — a deliberately slow scrambling algorithm that makes guessing impractical — before they ever touch the database. The readable key is shown exactly once, when you create it; after that there is no path — admin, support, ops — to recover it. A database breach surfaces scrambled values (hashes), not keys. To keep requests fast, a just-verified key is remembered for 30 seconds in a protected in-memory cache (sha256-keyed) — speed without weakening how keys are stored.
04 · Webhooks
Every webhook proves it came from us.
Signed (HMAC-SHA256) · stale messages rejected.
A webhook is a message we send to your systems when
something happens — say, a session finishing. Every delivery
carries a signature proving it came from us and wasn't
altered: the X-Driftstack-Signature header,
in the form t=<timestamp>,v1=<hex>.
Your code checks it with one SDK helper,
verifyWebhookSignature
(a constant-time compare, built to resist timing tricks).
Messages older than the default 5-minute timestamp
tolerance are rejected, so an intercepted copy can't be
re-sent later ("replay"); secrets rotate per-endpoint
without touching the rest of your account.
05 · Team roles (RBAC)
Your whole team can look. Only admins can change.
Admin / member roles, scope-gated writes.
Team members can see everything on the owner's account, but
only admin-role members can change anything (role-based
access control — RBAC). Sessions, profiles, webhooks, API
keys, email preferences — every change is permission-checked
before it runs (the role check sits at the route layer), and
the log records who did it (the member) separately from
whose account it was (the owner). Team owners review who
did what on their account at any time via
/v1/account/audit-log.
06 · No-customer-data-access posture
Nobody at Driftstack can watch your sessions.
Driftstack staff cannot read your sessions — metadata only, by design.
Driftstack's control plane stores license metadata, session metadata (id, lifecycle status, timestamps), and aggregate usage counters. It does not store the session content itself. URLs visited, form data submitted, screenshots captured, copies of page content (DOM snapshots), browser cookies — none of it ever reaches our servers. For self-hosted deployments, even the metadata stays inside your network; only license-validity heartbeats — periodic "is this license still valid?" check-ins — reach our servers.
What we don't claim
Honest scope.
- No SOC 2. SOC 2 — the standard third-party security audit large customers ask their vendors for — is not yet certified. We'll start the audit when the audit cost matches the customer profile that asks for it. We say so plainly rather than implying compliance via vague "enterprise-grade" language.
- No ISO 27001. Same answer for this international security-management standard: what matters is running the practices it describes; the certificate itself follows when the audit fits the budget.
- Sub-processors are listed. Hetzner, Neon, Upstash, Cloudflare, Postmark, Sentry, Stripe, Anthropic, Moneybird, MacStadium. Full list with purpose + jurisdiction at /trust/sub-processors.
- Data residency is EU-default. Compute and database in the EU (Hetzner in Falkenstein, Germany + Neon EU + Upstash EU). Uploaded files (avatars, for example) sit on Cloudflare R2, which can replicate outside the EU. Session execution may run elsewhere under SCCs (the EU's Standard Contractual Clauses for lawful data transfer abroad) + the EU-US Data Privacy Framework. Detail in the same sub-processors list.
Defense in depth
Layered controls. No single line of defense.
Each layer below is independently effective; an attacker would have to bypass every one to reach customer keys or session content. We assume any single layer can fail and design the next layer to survive it.
Edge
Cloudflare TLS 1.3 + WAF
All inbound traffic terminates at Cloudflare's edge. Full (strict) SSL/TLS validation against Let's Encrypt origin certificates ensures the request actually reached our origin and not a man-in-the-middle. Bot Fight Mode + WAF rules block automated abuse before it reaches the origin.
Origin
A locked-down server (nginx + UFW + fail2ban)
Our server at Hetzner runs the nginx web server with modern encryption only (TLS 1.2 + 1.3, Mozilla's recommended intermediate cipher suite, HSTS preload-eligible). A firewall (UFW) leaves exactly three doors open: remote administration (port 22, SSH), a redirect that bounces old unencrypted requests (port 80) onto the encrypted door, and the encrypted door itself (port 443, TLS). Repeat attackers get their address banned automatically at the kernel level (fail2ban).
Application
Auth gate + scope check + rate limit
Every authenticated request passes three checks, in order:
is this API key real, and whose is it (auth)? Is this key
allowed to do this specific thing (scope check — granular
per-resource permissions like
read:sessions)? And has this
account sent too many requests too fast (rate limit —
per-account token buckets backed by Redis with Postgres
fallback)? A request that fails any check is refused on the
spot — it never reaches the rest of the system.
Data
Encryption at rest + isolation
API keys are one-way scrambled (scrypt-hashed) with a unique random ingredient per key ("per-key salts"), so recovering the original is impossible by design. Account passwords use the same slow scrambler (KDF). Secrets that must stay usable — two-factor login seeds (MFA TOTP secrets), Anthropic API keys you bring for the AI agent (BYOK), and the keys that authenticate desktop-app control commands (gui_control_keys) — live encrypted in AES-256-GCM ciphertext under an encryption key that exists only on our server, decrypted in-memory only at execution time and never logged. The database (Postgres) and cache (Redis) providers additionally encrypt everything they store ("encryption at rest").
Audit
Append-only customer audit log
Every customer-visible event lands in an audit log that can
only ever be added to — never edited, never deleted
(append-only): API key lifecycle, MFA enrollment, profile
changes, webhook secret rotations, login + password changes.
Customers export the full log via the dashboard or
/v1/account/audit-log/export
(your data-portability right under GDPR Article 20).
Observability
Sentry + structured logs
Server errors go to Sentry, our error-tracking service (EU region), with personal data scrubbed out first (PII scrubbing). Request logs are machine-readable technical records (Pino structured JSON), and unusually slow database queries are flagged automatically (the slow-query log). No customer request body or response body is logged — only metadata sufficient to reconstruct what happened.
Threat model
What we defend against. What we don't.
In scope
- • API key compromise via leaked secret. Mitigation: keys stored only as scrypt hashes; rotation flow with 24-hour grace (the old key keeps working for 24 hours so nothing breaks mid-switch); granular per-resource scopes mean a stolen key can only do what that key was allowed to do (a limited blast radius).
- • Webhook signature forgery. Mitigation: every delivery is cryptographically signed (HMAC-SHA256) and messages outside the 5-minute timestamp tolerance are rejected; while you rotate a secret we sign with both old and new (dual-signing during the grace window), so verification never breaks.
- • Session hijacking via cross-site or cross-account access. Mitigation: every route restricts lookups to your own account at the database layer; a probe at another account's data gets a plain "not found" (cross-account lookups return 404, never 403 — the response never even confirms the thing exists).
- • Brute-force auth attempts. Mitigation: per-IP rate limits on login and key-check endpoints; automatic IP bans at the origin host level (fail2ban).
- • Stolen browser session token (dashboard). Mitigation: MFA on sensitive ops; active-sign-ins page lets customers revoke; session tokens have configurable idle timeouts.
Explicitly out of scope
- → Customer's destination response content. The control plane stores session metadata only — never response bodies. Whatever your code does inside a session is your threat model, not ours.
- → Detection-vendor cat-and-mouse evolution. Our anti-detection posture is engine-level (Apple's WebKit source code, not a JavaScript patch layered on top at run time — a "shim") and continuously updated; we can't promise forever-perfect parity against every detection vendor. See the cumulative-rig methodology for what we do guarantee.
- → Customer's keyboard / network at the endpoint. We can't protect a leaked API key on a customer developer's laptop or a webhook secret pasted into a build server that leaks its settings (a misconfigured CI runner).
- → Nation-state actors with sub-processor access. We assume the EU sub-processors (Hetzner, Neon, Upstash, Cloudflare) operate under their published security posture; we don't add additional anti-nation-state defense layers at the application tier.
Supply chain
Locked stack. Pinned versions. Audited dependencies.
Locked stack
Node 22 LTS, TypeScript strict, Fastify, Drizzle, Postgres 17, Redis 7. The stack rarely changes; every update to an outside software component (a dependency-bump PR) is security-scanned (`npm audit` review) and must pass the full test suite (a green test gate) before it lands.
Dependabot
Automated dependency-update proposals, opened weekly across every ecosystem we ship — the server and web apps, the Python and Go SDKs, the desktop client, and the CI pipeline itself. Nothing merges unless the full automated test suite passes (CI); only the smallest class of update (bug-fix-only patch releases) may merge automatically, and anything bigger waits for human review.
Locked installs, gated deploys
Every dependency version is pinned in a lockfile checked into the repository, and CI installs exactly those pinned versions. A release reaches production only through a gated pipeline: staging first, then an explicit manual approval or a deliberately cut release tag. The pipeline health-checks the new build the moment it is live and automatically rolls back to the previous version if that check fails — and anyone can verify exactly which source-code revision production is running via our public /version endpoint.
Ask us directly
Specific question your security team is asking?
Email security@driftstack.dev with the question. We answer everything in writing — no NDAs to read a one-paragraph answer about scrypt parameters or TLS cipher suites.