Skip to content

TLS and HTTPS (custom domains)

Serve the kdeps API server and web server over HTTPS for a custom domain. Two modes:

Applies to workflow mode.

  1. Static certificates - you supply PEM certFile / keyFile
  2. Let's Encrypt - kdeps obtains and renews certificates with ACME for your domain

Works in workflow mode (HTTP API / web) and for any agent package that enables apiServer / webServer. Agent mode REPL does not listen for HTTPS itself.

Choose a mode

DNSkdeps hostTLS modeStaticPEMLetsEncryptReverseProxyport80port443certsacmeproxy
ModeWhen to use
Static PEMCorporate CA, existing cert pipeline, secrets already mounted
Let's EncryptPublic custom domain, automatic renew, no PEM files to manage
Reverse proxy (Caddy/Traefik/Nginx)Multi-service mesh, K8s Ingress - kdeps stays HTTP internally

Priority: if both static PEM and letsEncrypt are set, static PEM wins.

Static certificates

yaml
# workflow.yaml
settings:
  certFile: "/etc/certs/server.crt"
  keyFile: "/etc/certs/server.key"
  apiServer:
    hostIp: "0.0.0.0"
    portNum: 443
    routes:
      - path: /api/v1/chat
        methods: [POST]

Paths are top-level under settings (not under apiServer). Same certs apply to webServer when enabled.

Let's encrypt (custom domain)

kdeps uses Go autocert (ACME) against Let's Encrypt production or staging.

Minimal config

yaml
# workflow.yaml
settings:
  letsEncrypt:
    domain: api.example.com    # required primary hostname
    email: ops@example.com     # recommended for LE expiry notices
  apiServer:
    hostIp: "0.0.0.0"
    portNum: 443
    routes:
      - path: /api/v1/chat
        methods: [POST]

Full field reference

yaml
settings:
  letsEncrypt:
    domain: api.example.com           # primary host (required unless domains[] only)
    domains:                          # optional additional SANs
      - www.example.com
      - app.example.com
    email: ops@example.com            # ACME account contact
    cacheDir: /var/lib/kdeps/letsencrypt   # default: ~/.kdeps/letsencrypt
    staging: false                    # true = staging CA (untrusted by browsers; no prod rate limits)
    # httpChallengeAddr: ":80"        # default; set "" to disable HTTP-01 listener
  apiServer:
    hostIp: "0.0.0.0"
    portNum: 443
  # webServer also uses the same TLS settings when present
  # webServer:
  #   hostIp: "0.0.0.0"
  #   portNum: 443
FieldDefaultPurpose
domain-Primary hostname on the cert
domains[]Extra hostnames (SANs); if only these are set, first becomes primary
email""Let's Encrypt registration email
cacheDir~/.kdeps/letsencryptACME account keys + issued certs (must be writable)
stagingfalseUse LE staging directory
httpChallengeAddr":80"Bind address for HTTP-01; empty string disables it

DNS and ports

  1. Create A/AAAA records for every hostname in domain / domains pointing at this machine (or load balancer that forwards 80/443).
  2. Open TCP 80 (HTTP-01 challenge) and TCP 443 (HTTPS app + TLS-ALPN-01).
  3. Prefer listening on port 443 for production HTTPS.
  4. Ensure the process can write cacheDir (persist this volume in Docker/K8s so renewals keep working).

What kdeps starts

When letsEncrypt is active (and no static PEM):

  • HTTPS on the configured API/web listen address (ListenAndServeTLS with autocert GetCertificate)
  • HTTP on httpChallengeAddr (default :80) serving /.well-known/acme-challenge/...; other paths return a short plain-text hint (no open redirect)

Certificates renew automatically while the process is running and the cache directory is preserved.

Staging checklist

Before production, validate the path with staging:

yaml
settings:
  letsEncrypt:
    domain: api.example.com
    email: ops@example.com
    staging: true
  apiServer:
    hostIp: "0.0.0.0"
    portNum: 443

Browsers will warn (staging CA is untrusted). When green, set staging: false and clear or use a separate cacheDir so production does not reuse staging material.

Docker

Publish 80 and 443; mount a volume for the LE cache:

bash
kdeps bundle build myagent-1.0.0.kdeps --tag myregistry/myagent:latest

docker run --rm -p 80:80 -p 443:443 \
  -v kdeps-le:/var/lib/kdeps/letsencrypt \
  -e KDEPS_API_AUTH_TOKEN=... \
  myregistry/myagent:latest
yaml
# workflow.yaml (inside the package)
settings:
  letsEncrypt:
    domain: api.example.com
    email: ops@example.com
    cacheDir: /var/lib/kdeps/letsencrypt
  apiServer:
    hostIp: "0.0.0.0"
    portNum: 443

Alternatively mount static secrets as certFile / keyFile (see Docker reference).

Kubernetes

Expose Service ports 80 and 443, use a persistent volume for cacheDir, and set hostIp/port so the pod listens on 443.

yaml
settings:
  letsEncrypt:
    domain: api.example.com
    email: ops@example.com
    cacheDir: /var/lib/kdeps/letsencrypt
  apiServer:
    hostIp: "0.0.0.0"
    portNum: 443

Many clusters prefer Ingress TLS (cert-manager) and keep kdeps on plain HTTP inside the cluster. That remains fully supported - omit letsEncrypt and terminate TLS at the Ingress.

bash
kdeps export k8s myagent-1.0.0.kdeps --image REG/myagent:1 -o k8s.yaml
# Edit Service/Ingress to publish 80/443 and attach a PVC to cacheDir when using in-pod LE
kubectl apply -f k8s.yaml

Reverse proxy (optional)

If you already run Caddy, Traefik, or Nginx for TLS:

  • Leave kdeps on HTTP (hostIp: 127.0.0.1, default port)
  • Do not set letsEncrypt or PEM paths on kdeps
  • Proxy https://api.example.comhttp://127.0.0.1:16395

LLM server appliances

kdeps llm appliances expose OpenAI-compatible /v1. For a custom domain HTTPS front-end, either:

  • Put Caddy/Traefik/Nginx with Let's Encrypt in front of the appliance port (8000), or
  • Run a kdeps agent/API package with letsEncrypt that proxies to the appliance

The appliance recipes themselves do not embed ACME today.

Troubleshooting

SymptomCheck
Challenge failsDNS points here? Port 80 open to the internet?
Rate limited by LEUse staging: true while iterating; then production with clean cache
Permission denied writing certscacheDir writable by the kdeps user
Wrong certificate hostHostname in request must match domain / domains whitelist
Still HTTPConfirm no static empty cert paths; look for log starting HTTPS server / letsencrypt:

See also

Released under the Apache 2.0 License.