Skip to main content

API security headers

Driftstack's HTTP API sets a fixed, reviewable set of security headers on every response. This page documents what they are and why, so security reviewers don't have to derive it from a production capture.

Always-on transport headers

HeaderValueWhy
Strict-Transport-Security max-age=63072000; includeSubDomains; preload 2-year HSTS, eligible for the browser preload list. Closes first-visit downgrade attacks before TLS upgrade kicks in.
X-Content-Type-Options nosniff Browsers must not infer MIME types from content.
X-Frame-Options SAMEORIGIN Driftstack ships no embeddable UI; framing is disallowed by default.
Referrer-Policy no-referrer No referrer leakage to third parties.
X-DNS-Prefetch-Control off Disables speculative DNS for embedded URLs.

Cache-Control posture

PathCache-Control
/v1/* (caller-private default) no-store, private
/v1/agent-sessions/{id}/transcript (authenticated SSE) no-cache, no-store, private, no-transform
/v1/status, /v1/status/incidents, /v1/status/incidents/{id}, and /v1/status/sla (public JSON) public, max-age=30
/v1/status/stream (SSE) no-cache, no-transform

Caller-private /v1/* responses default to no-store, private. The authenticated transcript stream listed above adds no-cache and no-transform while retaining no-store, private; it also reflects only an allowed Origin and sends Vary: Origin. This is defense-in-depth so shared / proxy caches can't hold onto private payloads and browser back-forward cache can't serve stale state after logout. Public status reads opt into the public policies shown above.

CORS

The API uses an explicit allow-list of origins; SDK consumers can call from any origin because the SDK ships Authorization: Bearer … and never relies on browser-cookie auth. Pre-flight responses cache for 10 minutes. credentials: true stays on for the browser clients, but no route reads a cookie: signed-in calls, the dashboard's included, send Authorization: Bearer …; the two live event streams, which a browser cannot give a header, take the same token as a ds_token query parameter.

Access-Control-Allow-Origin: <reflected allow-list match>
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: authorization, content-type, x-request-id, stripe-signature, x-nowpayments-sig, idempotency-key, x-byok-anthropic-api-key, x-driftstack-account, x-driftstack-gui-control-key, x-driftstack-conversation-token, x-driftstack-desktop-build, last-event-id
Access-Control-Expose-Headers: x-request-id, idempotent-replayed, x-ratelimit-bucket, x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset, ratelimit-limit, ratelimit-remaining, ratelimit-reset, retry-after
Access-Control-Max-Age: 600
Access-Control-Allow-Credentials: true

Cross-Origin resource policy

Cross-Origin-Resource-Policy: cross-origin is set explicitly. The default helmet value (same-origin) would block legitimate SDK calls from third-party origins because the CORS layer is our boundary, not CORP.

What we don't set

Reporting a finding

Send disclosures to [email protected]. We do not publish a PGP key yet; if you would rather not send exploit details in plain text, email [email protected] first and we will arrange an encrypted channel. Our policy (no bug bounty; we credit researchers) is on the vulnerability-disclosure policy.