Authentication
All API and MCP requests require an API key in theAuthorization: Bearer cai_... header. Keys are:
- Hashed with bcrypt — only the
key_prefix(first 8 characters) is stored in plaintext for display - Shown once — the full key is returned only at creation time and cannot be retrieved
- Scoped — each key has a set of scopes that restrict what it can access (
alerts:read,alerts:write,workflows:execute,approvals:write,admin, etc.) - Expirable — keys can be set to expire after a specified duration
Encryption at Rest
Enrichment provider auth credentials (API keys, tokens, secrets) are encrypted at rest using theENCRYPTION_KEY environment variable. Credentials are decrypted only at the moment of use during enrichment execution.
Rate Limiting
Built-in rate limiting viaslowapi, applied to all endpoints:
| Scope | Default | Configuration |
|---|---|---|
| Authenticated requests | 60/min per API key | RATE_LIMIT_DEFAULT_PER_MINUTE |
| Unauthenticated requests | 10/min per IP | RATE_LIMIT_UNAUTHED_PER_MINUTE |
| Ingestion endpoint | 120/min per API key | RATE_LIMIT_INGEST_PER_MINUTE |
429 Too Many Requests responses include a Retry-After header.
When deployed behind a reverse proxy, set TRUSTED_PROXY_COUNT so rate limiting reads the correct client IP from X-Forwarded-For. Without this, all requests appear to come from the proxy IP.
Security Headers
ASecurityHeadersMiddleware applies security headers to all responses:
X-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockStrict-Transport-Security(whenHTTPS_ENABLED=true)Content-Security-Policy
Request Body Size Limits
Enforced before route handlers process the request:| Limit | Default | Configuration |
|---|---|---|
| General requests | 10 MB | MAX_REQUEST_BODY_SIZE_MB |
| Ingestion payloads | 5 MB | MAX_INGEST_PAYLOAD_SIZE_MB |
CORS
CORS is disabled by default. Enable it only if you need browser-based access:Webhook Signature Verification
Inbound webhooks from source systems can be verified using HMAC signatures. Each source has its own signature header and secret:| Source | Signature Header | Secret Env Var |
|---|---|---|
| Sentinel | X-Webhook-Signature | SENTINEL_WEBHOOK_SECRET |
| Elastic | X-Webhook-Signature | ELASTIC_WEBHOOK_SECRET |
| Splunk | X-Splunk-Signature | SPLUNK_WEBHOOK_SECRET |
hmac.compare_digest() to prevent timing attacks.
Auth Failure Logging
All authentication failures are logged with structured context in a single location (app/auth/audit.py). Log entries include:
- Timestamp
- Request path and method
- Client IP
- Key prefix (if provided)
- Failure reason
LOG_FORMAT=json, these entries are machine-parseable for ingestion into your SIEM.
Secrets Management
By default, secrets are read from environment variables or a.env file. For production deployments, two cloud backends are supported:
- Azure Key Vault — set
AZURE_KEY_VAULT_URL, uses Managed Identity - AWS Secrets Manager — set
AWS_SECRETS_MANAGER_SECRET_NAME, uses IAM roles
Workflow Sandboxing
Workflow code is validated via AST analysis at save time. Only Python standard library andcalseta.workflows imports are allowed. File system access, subprocess calls, and third-party packages are blocked. All external interactions go through the provided ctx.http client.
API Key Source Restrictions
API keys can be restricted to specific alert sources viaallowed_sources. A key with allowed_sources: ["sentinel"] can only ingest alerts through the Sentinel source — attempts to use other sources are rejected with 403 Forbidden.
