Skip to main content
DRIFTSTACK

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.

  • Capture retention (roadmap)

    Today, screenshots and captures are returned directly inside the API response (inline bytes) and are not kept on our servers afterwards — so there is no retention setting to configure yet. Stored session recordings, with controls over how long they're kept, are on the roadmap — see /docs/recordings.

  • 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 pen-test evidence or compliance certifications, see /trust/compliance. Anything else, just email.

Email security