Skip to content

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.

incoming requestauth check401 if Bearer / X-Api-Key wrong or missingrate limit429 if over requestsPerMinute + burstbody size check413 if body exceeds maxBodyBytesconcurrency cap503 if over maxConcurrent in-flight requestsworkflow DAG

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:

yaml
# 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 here

Credential-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):

yaml
# ~/.kdeps/config.yaml
api_auth_token: "your-secret-token"

Or via environment variable:

bash
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.

yaml
# 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: nosniff
  • X-Frame-Options: DENY
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-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.

yaml
# workflow.yaml
settings:
  apiServer:
    rateLimit:
      requestsPerMinute: 60
      burst: 10

For webServer-only workflows, put the same fields under webServer:

yaml
# 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.

yaml
# workflow.yaml
settings:
  apiServer:
    maxBodyBytes: 1048576   # 1 MiB

TLS

Enable HTTPS by pointing certFile and keyFile at a PEM certificate and private key. These fields belong in settings, not in apiServer.

yaml
# 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.

yaml
# workflow.yaml
settings:
  apiServer:
    maxConcurrent: 50

Build-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:

bash
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):

yaml
# deploy/env-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: my-agent-env
type: Opaque
stringData:
  OPENAI_API_KEY: "sk-..."
yaml
# fragment from kdeps export k8s output
env:
  - name: OPENAI_API_KEY
    valueFrom:
      secretKeyRef:
        name: my-agent-env
        key: OPENAI_API_KEY

Resource Output Caps

Four environment variables limit how many bytes executor resources return to the workflow engine. Set them in agentSettings.env.

VariableApplies to
KDEPS_EXEC_MAX_OUTPUT_BYTESShell / exec resource stdout
KDEPS_HTTP_MAX_RESPONSE_BYTESHTTP resource response body
KDEPS_CHAT_MAX_OUTPUT_BYTESLLM chat response content
KDEPS_PYTHON_MAX_OUTPUT_BYTESPython resource stdout
yaml
# 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 KiB

See Also

Released under the Apache 2.0 License.