Trust center · Security overview
Every claim, mapped to evidence.
Architecture deep-dive lives at /security; this page is what you skim before scheduling a vendor review call. Each claim links to the code, test, or doc you can verify for yourself.
Authentication & access
- ✓
API keys are scrypt-hashed at rest
The readable key is never written to any storage, anywhere — it's one-way scrambled (scrypt) first. The scrambler's strength settings (hash params N=2^15, r=8, p=1) live alongside the hash so keys can be verified without ever storing the key itself. A database breach surfaces hashes, not credentials.
apps/server/src/lib/api-keys.ts · hashApiKey() / verifyApiKey()
- ✓
MFA: TOTP + recovery codes
Two-factor login uses authenticator-app codes (TOTP). The seed that generates your codes is stored encrypted (AES-256-GCM at-rest encryption of TOTP secrets). Recovery codes are scrypt-hashed — one-way scrambled, mirroring API key handling. Dangerous admin actions demand a fresh MFA check even if you're already signed in (a "step-up" gate on destructive admin paths).
apps/server/src/lib/mfa-totp.ts · apps/server/src/services/mfa.ts
- ✓
OAuth 2.0 (invite-only) with PKCE-S256
Third-party app access (OAuth) requires admin invitation (no self-service client registration). Every connection uses PKCE-S256 — an extra proof that the app finishing a login is the same one that started it. App secrets (client_secret) are sha256-hashed at rest; login codes work exactly once (one-shot authorization codes); and access tokens are opaque random strings (no JWT), so a stolen token carries no readable data.
apps/server/src/services/oauth.ts · apps/server/src/lib/oauth-pkce.ts
- ✓
BYOK Anthropic keys + gui_control_keys + Mac LiveKit secrets: AES-256-GCM at rest
Three kinds of secrets that would be as damaging as passwords if leaked ("plaintext-equivalent" material) live in AES-256-GCM ciphertext under the same host-resident encryption key as MFA TOTP secrets: (1) your own Anthropic API key, if you bring one for the AI agent (BYOK — bring your own key); (2) the per-session keys that authenticate desktop-app control commands (gui_control_keys — HMAC keys); and (3) the LiveKit secrets each Mac in our fleet registers via POST /v1/mac-nodes/register, which the platform uses to create the short-lived video-access tokens (JWTs) the desktop app needs to watch a live session. All three are decrypted in-memory only at execution time, never logged, never echoed in responses. Our logging and error-reporting layers also scrub secrets automatically (Pino-redact + Sentry-scrub) in case one ever ends up inside a message.
apps/server/src/services/byok-anthropic.ts · apps/server/src/lib/byok-anthropic-encryption.ts · apps/server/src/lib/livekit-secret-encryption.ts
Transport & egress
- ✓
TLS 1.3 on every customer-facing path
Cloudflare, our edge network, enforces strict TLS 1.3 encryption all the way to our own servers behind
api.driftstack.dev+app.driftstack.dev(the origins). No unencrypted page (plaintext HTTP) exists on any path — every release is automatically checked for this before it ships; the deploy pipeline's TLS check rejects the release otherwise. - ✓
Customer-configurable egress (per profile)
Each profile can attach its own egress — its own exit to the internet: a SOCKS5 proxy with full UDP/WebRTC/QUIC tunnelling (the video-call and modern-web traffic types 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 launches, and address-lookup traffic can't slip out around it (DNS leaks blocked). Without an attached config, session traffic exits via Driftstack's own EU network egress. Driftstack does not log session-traffic payloads (the destination URLs you visit, the page content that comes back); the proxy layer passes traffic through without storing it.
Webhooks & integrations
- ✓
Outbound webhooks are HMAC-SHA256 signed
A webhook is a message we send to your systems when something happens. Every delivery carries
X-Driftstack-Signature— a cryptographic signature (HMAC) over the timestamp + message body. Your code verifies it via one SDK helper; messages older than the allowed timestamp tolerance window are rejected, so an intercepted copy can't be re-sent later (replay attacks).apps/server/src/lib/webhook-signing.ts
- ✓
Inbound webhooks (Stripe, NowPayments) signature-verified before any state mutation
Payment notifications must prove they're genuine before we change anything on your account. Stripe: timestamp + sha256 HMAC. NowPayments: HMAC-SHA512 over the normalised (canonical-keyed) JSON. A shared raw-body parser ensures the exact bytes the signature was computed over are the bytes the verifier sees — nothing can be altered in transit without detection.
apps/server/src/lib/stripe-signing.ts · apps/server/src/lib/nowpayments-signing.ts
Data residency & retention
- ✓
EU control plane
Compute (Hetzner Nuremberg), database (Neon Frankfurt), object storage (Cloudflare R2, EU + US replication). The iPhone Safari session-execution fleet runs on MacStadium hardware (US) under SCCs (the EU's Standard Contractual Clauses for lawful data transfer abroad) + the EU-US Data Privacy Framework — see data residency. Full sub-processor list at /trust/sub-processors.
- ✓
Direct captures and local recordings
Screenshots and captures are returned directly inside the API response as inline bytes and are not retained by the capture endpoint. The desktop recorder stores streamed frames on the operator's machine and does not upload them through the recording workflow. See /docs/recordings for the exact storage boundary.
- ✓
Account deletion: 30-day grace, then hard delete
For 30 days after cancellation your data is only flagged as deleted ("soft-delete") and can be restored if you come back. After that it is permanently erased (hard delete) — profile data, sessions, captures. Per our DPA.
Observability & incident response
- ✓
Public incident history + status page
Customer-impacting outages are posted publicly within the incident-response SLA window. Live status at status.driftstack.dev; historical archive at /trust/incidents.
- ✓
Vulnerability reports: acknowledged in 2 days, assessed in 5 (triage)
We won't take legal action against good-faith research ("safe-harbour"). Reporters agree to keep a finding private for 90 days while we fix it (the coordinated disclosure window), extendable on agreement. Full policy at /trust/compliance.
- ✓
We rehearse failures on purpose (chaos engineering)
Vendor (sub-processor) outages, the database switching to its backup (DB failover), the Redis cache going down, webhook-signature failures — all covered by scripted drills in
scripts/chaos/. Drills run as simulations by default (dry-run); actually breaking things (execute mode) requires explicit operator opt-in.scripts/chaos/
Still need a deeper look?
The architecture deep-dive at /security walks the six-pillar surface in detail. For current compliance status and vulnerability reporting, see /trust/compliance. Anything else, just email.