If you discover a security vulnerability in Styx, please report it responsibly:
- Email: security@styxhq.com
- GitHub: Security Advisories (private disclosure)
Please do NOT report security vulnerabilities through public GitHub issues.
- Acknowledgment within 48 hours
- Initial assessment within 5 business days
- Critical vulnerabilities patched as quickly as possible
This policy covers:
- Styx gateway (Go router)
- Python backend API
- MCP server
- Dashboard (Next.js)
- Official packages (npm: styx-mcp-server)
| Version | Supported |
|---|---|
| Latest | Yes |
| < Latest | No |
Styx implements defense-in-depth across all layers.
- Provider API keys are encrypted using Fernet (AES-128-CBC + HMAC-SHA256) via the
cryptographylibrary - Versioned ciphertext format (
v1:{token}) supports seamless key rotation withENCRYPTION_KEYandENCRYPTION_KEY_PREVIOUS - Styx API keys are hashed with HMAC-SHA256 using a server-side secret (
API_KEY_HMAC_SECRET) — plaintext keys are returned once at creation and never stored - Key prefixes (first 15 chars) are stored separately for display; full keys cannot be recovered
- JWT-based authentication via Supabase Auth (RS256 with JWKS, HS256 fallback)
- Token blacklisting in Redis on logout with TTL-based expiry
- API key validation cached in the Go router with SHA-256 in-memory hashing, bounded LRU (10,000 entries, 30s TTL)
- Real-time key revocation via Redis pub/sub
- Supabase webhook signature verification (HMAC-SHA256)
- Backend: Redis-backed sliding window (Sorted Sets) — 5 req/min on login, 3 req/min on registration
- Router: Per-API-key rate limiting via atomic Redis Lua scripts (prevents TOCTOU races)
- nginx: Zone-based rate limiting (30 req/s API, 5 req/min auth)
- End-user rate limiting with
X-End-User-Idheader support - Standard
Retry-AfterandX-RateLimit-*response headers
- TLS termination at nginx with TLSv1.2 and TLSv1.3 only
- ECDHE cipher suites (AES-128-GCM, AES-256-GCM)
- HSTS enabled (2 years, includeSubDomains, preload)
- HTTP-to-HTTPS redirect (301)
- cert-manager integration for automatic Let's Encrypt certificates in Kubernetes
Applied at both the Go router and nginx layers:
X-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockReferrer-Policy: strict-origin-when-cross-originPermissions-Policy(disables camera, microphone, geolocation, payment)Content-Security-Policywith restrictive directives (default-src 'self',frame-ancestors 'none')Cache-Control: no-storeon API responses
- Go router: Regex validation on model names (blocks path traversal,
..), request IDs (blocks log injection), and allowed API paths (whitelist) - Python backend: Pydantic models with EmailStr, password complexity (uppercase, lowercase, digit, special char), and field length constraints
- Request body size limit: 10 MB
- Streaming response size limit: 50 MB
- CORS origins configured via environment variable; disabled if not set
- CSRF protection via Origin header validation on mutating requests
- Internal endpoints exempt (authenticated via
X-Internal-Secret) - Non-browser clients (SDKs, CLIs) allowed through without Origin
- Webhook URLs validated against blocked networks:
127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16(AWS metadata), IPv6 loopback and link-local - Hostname blocklist:
localhost,metadata.google.internal,*.internal,*.local - DNS resolution timeout: 10 seconds
- Per-provider circuit breaker (Closed / Open / Half-Open states)
- Configurable failure threshold (default: 5 consecutive failures)
- Automatic provider failover with health check probes (15s interval)
- Half-open recovery with single test request
- Append-only audit trail:
{resource}.{verb}format (e.g.,api_key.create,user.login) - Captures actor ID, email, IP, action, resource, status
- Fire-and-forget — never breaks business logic
- No PII in audit detail fields
- Compiled regex patterns detect email addresses, phone numbers, and credit card numbers in responses
- Request and response bodies are never logged — only metadata (status, latency, model, provider)
- Structured logging with request ID correlation
- Kubernetes NetworkPolicies enforce east-west traffic restrictions:
- Classifier and cache service accept traffic only from the router
- Databases accept traffic only from authorized services
- External traffic restricted to HTTPS (port 443) with private ranges blocked
- All secrets via environment variables — never hardcoded
- Kubernetes Secrets with validation (fails if required secrets missing)
- External Secrets Operator integration (AWS Secrets Manager, HashiCorp Vault)
- Secret generation documented:
openssl rand -base64 32for HMAC keys,Fernet.generate_key()for encryption
- Bandit security linter configured (
.bandit.yaml) for injection, crypto, deserialization, and SQL injection checks - GitHub Actions security workflow for CI/CD scanning