Security Reference
Every request passes through a chain of gates before reaching the workflow DAG. Each gate can reject the request with a specific status code.
Authentication
When apiServer is configured, authentication is required. Every request must include the token via Authorization: Bearer <token> or X-Api-Key: <token>. The /health endpoint is always exempt. /_kdeps/* management routes use KDEPS_MANAGEMENT_TOKEN instead (see Management API). kdeps refuses to start the API server without a token.
Public routes
Routes serving a browser frontend can opt out of bearer auth with public: true - a static page cannot hold a secret, so anything shipped in JS would be public anyway:
# workflow.yaml
settings:
apiServer:
routes:
- path: /api/v1/chat
methods: [POST]
public: true # no token needed for credential-less requests
- path: /api/v1/admin
methods: [POST] # token still required hereCredential-less requests pass; a presented token is still validated, so a wrong Authorization header always gets 401. In merged API+Web mode the webServer routes themselves are always public (a browser navigation cannot send a header), but paths claimed by an API route stay authenticated unless marked public: true.
Set the token in ~/.kdeps/config.yaml (machine-local, never committed):
# ~/.kdeps/config.yaml
api_auth_token: "your-secret-token"Or via environment variable:
export KDEPS_API_AUTH_TOKEN="your-secret-token"No auth: block in workflow.yaml. The token is never stored in the workflow file.
Trusted Proxies
X-Forwarded-For and X-Real-IP are ignored unless the direct TCP peer matches an entry in trustedProxies. This prevents clients from spoofing their IP for rate limiting and request context. Configure CIDRs or exact IPs for your load balancer or ingress.
# workflow.yaml
settings:
apiServer:
trustedProxies:
- "10.0.0.0/8"
- "172.16.0.0/12"
- "192.168.0.0/16"Without trustedProxies, kdeps uses RemoteAddr only. When both apiServer and webServer are configured, entries from both blocks are merged for rate limiting and request IP context.
Security Headers
Both apiServer and webServer responses include defensive HTTP headers on every response:
X-Content-Type-Options: nosniffX-Frame-Options: DENYReferrer-Policy: strict-origin-when-cross-originPermissions-Policy: camera=(), microphone=(), geolocation=()Strict-Transport-Security: max-age=31536000; includeSubDomains(TLS only)
apiServer also sets a strict Content-Security-Policy (default-src 'none'; frame-ancestors 'none'; base-uri 'none') on JSON API responses. webServer omits CSP so proxied apps (Streamlit, Gradio, SPAs) are not blocked.
webServer-only mode
When only webServer is configured (no apiServer), kdeps does not enforce bearer auth. Static files and app proxies are world-readable if bound to 0.0.0.0 unless you add an ingress with auth. You can still set rateLimit, maxBodyBytes, and maxConcurrent under webServer to throttle abuse.
Rate Limiting
Limit requests per client IP using a token-bucket algorithm. requestsPerMinute is the sustained rate; burst is the number of requests allowed above that rate in a single burst. Clients that exceed the limit receive a 429 response with a Retry-After: 60 header.
# workflow.yaml
settings:
apiServer:
rateLimit:
requestsPerMinute: 60
burst: 10For webServer-only workflows, put the same fields under webServer:
# workflow.yaml
settings:
webServer:
rateLimit:
requestsPerMinute: 120
burst: 20
maxBodyBytes: 1048576
maxConcurrent: 50
routes:
- path: "/"
serverType: "static"
publicPath: "./public"Body Size Limit
Cap the size of incoming request bodies. Requests that exceed maxBodyBytes receive a 413 response. This limit does not apply to multipart/form-data uploads.
# workflow.yaml
settings:
apiServer:
maxBodyBytes: 1048576 # 1 MiBTLS
Enable HTTPS by pointing certFile and keyFile at a PEM certificate and private key. These fields belong in settings, not in apiServer.
# workflow.yaml
settings:
certFile: "/etc/certs/server.crt"
keyFile: "/etc/certs/server.key"
apiServer:
routes:
- path: /api/v1/chat
methods: [POST]Concurrent Request Limit
Cap the number of simultaneous in-flight requests. When the limit is reached, new requests receive 503 Service Unavailable immediately rather than queuing. Omit or set to 0 to disable.
# workflow.yaml
settings:
apiServer:
maxConcurrent: 50Build-Time Env (Docker and Kubernetes Export)
agentSettings.env is embedded into generated Dockerfiles and Kubernetes manifests. Plain keys become ENV / value: entries. kdeps rejects auth tokens at Docker build time (KDEPS_API_AUTH_TOKEN, KDEPS_MANAGEMENT_TOKEN) and any secret-like key (_TOKEN, _SECRET, _PASSWORD, _API_KEY, etc.) in both Docker and Kubernetes export.
Set those at runtime instead:
docker run -e KDEPS_API_AUTH_TOKEN=api-secret -e KDEPS_MANAGEMENT_TOKEN=mgmt-secret -e OPENAI_API_KEY=sk-...Kubernetes export maps auth tokens to {metadata.name}-auth and secret-like agentSettings.env keys to {metadata.name}-env via secretKeyRef (values are never written into the manifest):
# deploy/env-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: my-agent-env
type: Opaque
stringData:
OPENAI_API_KEY: "sk-..."# fragment from kdeps export k8s output
env:
- name: OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: my-agent-env
key: OPENAI_API_KEYResource Output Caps
Four environment variables limit how many bytes executor resources return to the workflow engine. Set them in agentSettings.env.
| Variable | Applies to |
|---|---|
KDEPS_EXEC_MAX_OUTPUT_BYTES | Shell / exec resource stdout |
KDEPS_HTTP_MAX_RESPONSE_BYTES | HTTP resource response body |
KDEPS_CHAT_MAX_OUTPUT_BYTES | LLM chat response content |
KDEPS_PYTHON_MAX_OUTPUT_BYTES | Python resource stdout |
# workflow.yaml
settings:
agentSettings:
env:
KDEPS_EXEC_MAX_OUTPUT_BYTES: "524288" # 512 KiB
KDEPS_HTTP_MAX_RESPONSE_BYTES: "1048576" # 1 MiB
KDEPS_CHAT_MAX_OUTPUT_BYTES: "1048576" # 1 MiB
KDEPS_PYTHON_MAX_OUTPUT_BYTES: "524288" # 512 KiBSee Also
- Advanced Configuration - Request object, agent settings, SQL connections, trusted proxies
- Docker Reference - Container security hardening
- Validation and Control Flow - Per-resource access control
