Skip to content

klangkd Configuration File

The klangkd launcher reads configuration from a YAML file, environment variables, and built-in defaults. The config file is the primary substrate for deployment settings — environment variables remain supported as overrides.

Precedence

Values are resolved in this order (highest priority first):

  1. Environment variables (KLANGK_*) — override everything
  2. Config file — the YAML file passed to --config
  3. Built-in defaults — hardcoded in the settings model

This means an operator can set the bulk of a deployment's config in the YAML file and still tweak individual values via env vars (e.g. in CI, ansible, or --env-file) without rewriting the file.

--config flag

klangkd resolves its config file in three modes:

Invocation Behavior
klangkd Resolves $KLANGKD_CONFIG_DIR/klangkd.yaml (default ~/.config/klangkd/klangkd.yaml). If the file is missing it is generated as a near-empty template pointing at the docs. No admin identity or password is emitted — the admin row is seeded at runtime: default_user defaults to <unixuser>@example.com, with password_hash=None in none/oidc mode (no password needed) and KLANGKD_DEFAULT_PASSWORD required in password/both mode (fail-fast if unset).
klangkd --config /path/to/config.yaml Uses the specified file. Missing → startup error. Explicit paths are never auto-generated.
klangkd --config=none No config file — env vars and built-in defaults only.

A bare klangkd is the pip/uv first-run path: install the wheel, run klangkd, log in with the printed credentials. System deployments that manage config out-of-band typically use --config=none (the host container's supervisord.conf does this) or --config=<explicit-path> (a deployment pipeline writes the file before starting klangkd).

Key mapping

Config-file keys map directly to KLANGK_* environment variable names with the prefix stripped and lowercased. For example:

Env var Config-file key
KLANGKD_JWT_SECRET jwt_secret
KLANGKD_EGRESS_PORT egress_port
KLANGKD_AUTH_MODES auth_modes

snake_case or kebab-case

Every config-file key accepts either snake_case or kebab-case — jwt_secret and jwt-secret resolve to the same setting, as do egress_port / egress-port, auth_modes / auth-modes, and so on. This matches the dual-form lookup the OIDC provider dicts already had and is forgiving of either style, but snake_case is the preferred/documented form — all examples in this chapter (and the field names they map to) use snake_case.

Native scalar types

Numeric, boolean, and list fields accept their natural YAML scalar types — a bare number for numeric fields (access_token_hours: 48, port: 8997), a bare true/false for boolean fields (allow_sudo: true), and a bare list for list fields. Quoted strings (port: "8997") keep working everywhere; both forms parse identically. Env vars are always strings and behave exactly as before.

file: and cmd: resolution

Any value — whether from the config file or an env var — can use file: or cmd: prefixes to resolve secrets at construction time (not per-call at use time):

jwt_secret: "file:/run/secrets/jwt"
smtp_password: "cmd:aws secretsmanager get-secret-value --secret-id klangk/smtp | jq -r .SecretString"

The resolver is source-agnostic: it sees only the value, not where it came from. KlangkSettings runs every string field through the resolver once, in a model_validator(mode="after"), before the object leaves __init__. A bad reference (file:/nonexistent, cmd:false) fails fast at boot with a ValidationError — not silently at use time. Thereafter every settings.field read returns the already-resolved value; no caller wraps in a resolver call.

Inline OIDC providers

OIDC providers can be specified directly in the config file under the oidc_providers key, removing the need for a separate OIDC config file:

auth_modes: both

oidc_providers:
  - id: cac
    display-name: CAC Login
    issuer: https://keycloak.example.com/realms/company
    client-id: klangk
    client-secret: "file:/run/secrets/cac-secret"
    scopes: openid email profile
    ca-cert: /etc/pki/tls/certs/company-ca-bundle.pem
    logout-redirect: true

  - id: internal
    display-name: Internal SSO
    issuer: https://keycloak.example.com/realms/corp
    client-id: klangk
    client-secret: "file:/run/secrets/corp-secret"
    trust-email: true

If KLANGKD_OIDC_CONFIG is also set (as an env var), the separate file wins — consistent with the global precedence rule (env vars override config-file values). When KLANGKD_OIDC_CONFIG is unset, the inline oidc_providers list is used. See OIDC Configuration for provider field details.

Feature configuration (features_config:)

Feature-declared config keys (the ones the build emits into features.json — the container_env_keys list plus the per-feature config blocks) are outside the KLANGK_ settings model: their names aren't known at settings construction, so they're resolved per-key at use time by resolve_dynamic_config. That resolver had one value source — the server's environment. features_config: adds a second source so long-lived deploy config (OAuth client IDs, RAG endpoints) can live in the committed klangkd.yaml instead of an env var:

features_config:
  github_oauth_client_id: "abc123"
  soliplex_url: "https://rag.example.com"

Each key may be written as the stripped, lowercased short form — e.g. soliplex_url, the same key the frontend receives via GET /api/v1/config — or the full declared name (KLANGKWS_FEATURE_SOLIPLEX_URL, the same string that appears in features.json). The short form is preferred; both resolve identically. Precedence when a feature key is resolved: env > features_config: > feature-declared default. Env stays the escape hatch for per-invocation overrides; the block carries the durable deploy values; the feature default is the floor.

file: / cmd: prefixes work on values in this block too — they're honored per-key at resolution time, consistent with how the same resolver treats env values. Unlike top-level KLANGK_* fields, a bad file:/cmd: reference here does not fail at boot (the values can't be resolved at construction); it logs and falls through to the feature default — the same behavior a broken env ref already has.

This block is read at boot and on SIGHUP (reloadable, like the rest of the file). It supplies values only; it does not change which features are compiled in (build-time) or which are turned on (KLANGKD_FEATURES_ENABLE, deploy-time).

Quote non-string values. The block is typed as a plain string map and is resolved outside the typed settings model (resolve_dynamic_config), so quote numerics and booleans here — soliplex_port: "8080", not 8080. (Top-level settings fields, unlike this block, accept their native YAML scalar types — see above.)

JSON-valued keys: use a block scalar. A value that is itself JSON — e.g. oauth_providers, the git-credential device-flow provider map — must arrive as one string, and a native YAML list or mapping as the value is rejected at construction. Avoid the double-quote-escaping trap entirely with a folded block scalar (>-; JSON is whitespace-insensitive, so line breaks fold cleanly) or a single-quoted one-liner (double quotes inside need no escaping), or keep the JSON in its own file and reference it with the file: prefix:

features_config:
  oauth_providers: >-
    [{"host": "gitlab.example.com",
      "client_id": "abc123",
      "device_code_url": "https://gitlab.example.com/oauth/authorize_device",
      "token_url": "https://gitlab.example.com/oauth/token",
      "scope": "read_repository write_repository",
      "username": "oauth2"}]
  # …or:
  # oauth_providers: file:/etc/klangk/oauth-providers.json

Complete example

A production-ready config file covering all common settings:

# /etc/klangkd.yaml

# --- Auth / identity ---
auth_modes: both
jwt_secret: "file:/run/secrets/jwt"
prevent_insecure_jwt_secret: true
default_user: admin@example.com
default_password: "file:/run/secrets/admin-pw"
access_token_hours: 24
min_password_length: 12
password_require_upper: 1
password_require_lower: 1
password_require_digit: 1
password_require_special: 1
password_history_count: 5
password_min_changed: 8
# Password-age hardening posture (#3177): keep a password 24h
# before it may be changed again; expire it after 60 days.
password_min_age_hours: 24
password_max_age_days: 60

# --- Server / network ---
listen: "127.0.0.1"
port: 8997
egress_port: 8995
api_rate_limit: 300 # opt-in per-client-IP /api budget (0 = off, default)
hosting_hostname: klangk.example.com
hosting_proto: https
trusted_proxy_cidrs: "127.0.0.1,::1,10.0.0.0/8"

# --- Storage (required, no defaults) ---
state_dir: /var/lib/klangk/state
data_dir: /var/lib/klangk/data

# --- Container / workspace ---
image_name: klangk-workspace
image_pull_policy: missing
port_range_start: 9000
allow_sudo: true

# --- OIDC (inline) ---
oidc_providers:
  - id: company-sso
    display-name: Company SSO
    issuer: https://sso.example.com/realms/main
    client-id: klangk
    client-secret: "file:/run/secrets/oidc-secret"
    trust-email: true

# --- SMTP ---
smtp_host: smtp.example.com
smtp_port: 587
smtp_user: klangk@example.com
smtp_password: "file:/run/secrets/smtp-pw"
smtp_from: noreply@example.com
smtp_use_tls: true

# --- Admin notifications (#3250) ---
# SA/ISSO recipients for account lifecycle and audit alerts. Both
# channels are optional; with neither set, notifications are off.
admin_notification_emails:
  - isso@example.com
  - sa@example.com
admin_notification_webhook_url: https://hooks.example.com/klangk
# Narrow the allowlist (defaults to every supported event):
# admin_notify_events:
#   - user.create
#   - user.delete
#   - user.disable
#   - user.enable

# --- Audit-record forwarding (#3252) ---
# Ship every new row of the three audit tables to a SIEM. Both targets
# are optional; each receives every record. With neither set, the
# forwarder is off.
# audit_forward_url: https://siem.example.com/ingest
# Optional auth header for the URL target:
# audit_forward_header: "Authorization: Bearer hec-token"
# audit_forward_syslog: tls://siem.example.com:6514

# --- Branding ---
product_name: "My Platform"
brand_color: "#1565C0"
logo_url: https://example.com/logo.png
# Require consent banner acknowledgement on every fresh app load / login
login_banner_every_visit: true

All configuration keys

Every key below corresponds to a KLANGK_* environment variable (uppercased, with KLANGK_ prefix). See Environment Variables for detailed descriptions of each.

Deployment shape (derived from KLANGKD_PORT + KLANGKD_AUTH_MODES)

The deployment shape is derived from two knobs: the browser port (KLANGKD_PORT) and the auth gate (KLANGKD_AUTH_MODES). klangk picks the proxy template and enforces its one safety rule from their combination — there is no separate "mode" to set.

KLANGKD_PORT is the browser/proxy port. Unset ⇒ headless (no browser listener is rendered; the proxy serves only the container-egress listener on KLANGKD_EGRESS_PORT). Set ⇒ full/browser mode — the proxy serves the browser UI, API, WebSocket, and hosted apps on listen {KLANGKD_LISTEN}:{KLANGKD_PORT}; plus a separate container-egress listener on KLANGKD_EGRESS_PORT.

KLANGKD_AUTH_MODES is the sole authority on the auth gate (none / password / oidc / both). When unset it defaults to none; OIDC settings never promote it.

For each combination, klangk renders the maximum-feature proxy template the combination can service (the proxy is Caddy, the sole engine):

KLANGKD_PORT KLANGKD_AUTH_MODES proxy template browser? status
unset (headless) none headless no ✅ (most secure)
unset (headless) password / oidc / both headless no ✅ (most secure, less convenient)
loopback TCP none full yes (local) ✅ (local-dev "just works")
loopback TCP password / oidc / both full yes (local) ✅
non-loopback TCP none — — ⚠️ rejected unless KLANGKD_ALLOW_INSECURE_NO_AUTH=1
non-loopback TCP password / oidc / both full yes (net) ✅ (least secure; operator opted in)
  • Template selection keys off KLANGKD_PORT only: unset ⇒ headless (container-egress only — no browser UI); set ⇒ full (browser UI + API + hosted apps). KLANGKD_AUTH_MODES does not change which template renders.
  • The one gate (none on non-loopback KLANGKD_LISTEN) is enforced by enforce_no_auth_bind_safety() at boot — refused unless KLANGKD_ALLOW_INSECURE_NO_AUTH=1 is set (e.g. a throwaway VM on an isolated network). No-auth mode freely issues an admin token, so exposing it off-loopback is opt-in, not silent.

Security ordering (most → least secure): headless+password > headless+none

loopback TCP (local) > non-loopback TCP+gate. Headless is the most-secure posture: the backend binds only a UDS (same-uid socket access), and the proxy serves only the container-egress listener — no browser/TCP surface at all. (The proxy engine is Caddy, the sole engine.)

Key Default Env var
listen 127.0.0.1 KLANGKD_LISTEN
# --- Deployment shape ---
# port is the browser/proxy port. Unset ⇒ headless (no browser listener).
# Set ⇒ full/browser mode (UI + API + hosted apps).
port: 8997
# listen is the browser interface address (rendered only when port is set).
# A bare IP literal or host name — the port lives in `port`, never here
# (Caddy ignores a port written inside a bind address); anything else
# fails startup (#3275).
# listen: "127.0.0.1"  # browser interface address (default loopback; set 0.0.0.0 for all interfaces)
# egress_listen is the egress interface address. Defaults to 0.0.0.0 (all
# interfaces) — the only portable default across podman network modes. The
# three served egress locations are double-gated (CONTAINER_ACL deny-all →
# 403 outside the container subnet, plus auth_request workspace-token → 401
# without a valid JWT), so the all-interfaces bind is not a security hole.
# Pin to a specific host IP if your container-facing interface is stable.
# The address grammar matches `listen` (#3275).
# egress_listen: "0.0.0.0"
# auth_modes is the sole auth authority; unset defaults to none.
# auth_modes: password  # or oidc / both / none

Auth / identity

Key Default Env var
auth_modes none KLANGKD_AUTH_MODES
jwt_secret (insecure dev default) KLANGKD_JWT_SECRET
audit_hmac_key (unset — tagging disabled) KLANGKD_AUDIT_HMAC_KEY
prevent_insecure_jwt_secret KLANGKD_PREVENT_INSECURE_JWT_SECRET
default_user <unixuser>@example.com KLANGKD_DEFAULT_USER
default_password KLANGKD_DEFAULT_PASSWORD
access_token_hours 24 KLANGKD_ACCESS_TOKEN_HOURS
workspace_token_hours 24 KLANGKD_WORKSPACE_TOKEN_HOURS
min_password_length 8 KLANGKD_MIN_PASSWORD_LENGTH
password_require_upper 0 KLANGKD_PASSWORD_REQUIRE_UPPER
password_require_lower 0 KLANGKD_PASSWORD_REQUIRE_LOWER
password_require_digit 0 KLANGKD_PASSWORD_REQUIRE_DIGIT
password_require_special 0 KLANGKD_PASSWORD_REQUIRE_SPECIAL
password_history_count 0 KLANGKD_PASSWORD_HISTORY_COUNT
password_min_changed 0 KLANGKD_PASSWORD_MIN_CHANGED
password_min_age_hours 0 KLANGKD_PASSWORD_MIN_AGE_HOURS
password_max_age_days 0 KLANGKD_PASSWORD_MAX_AGE_DAYS
login_lockout_failures 5 KLANGKD_LOGIN_LOCKOUT_FAILURES
login_lockout_duration 900 KLANGKD_LOGIN_LOCKOUT_DURATION
login_lockout_window 300 KLANGKD_LOGIN_LOCKOUT_WINDOW
max_sessions_per_user 0 KLANGKD_MAX_SESSIONS_PER_USER
step_up_window_minutes 0 KLANGKD_STEP_UP_WINDOW_MINUTES
inactivity_disable_days 35 KLANGKD_INACTIVITY_DISABLE_DAYS
disable_registration KLANGKD_DISABLE_REGISTRATION
disable_invites KLANGKD_DISABLE_INVITES
invite_expire_hours 72 KLANGKD_INVITE_EXPIRE_HOURS
allow_insecure_no_auth KLANGKD_ALLOW_INSECURE_NO_AUTH
reject_proxy_headers KLANGKD_REJECT_PROXY_HEADERS
session_idle_timeout_minutes 0 KLANGKD_SESSION_IDLE_TIMEOUT_MINUTES
privileged_session_idle_timeout_minutes 10 KLANGKD_PRIVILEGED_SESSION_IDLE_TIMEOUT_MINUTES
session_workstation_binding off KLANGKD_SESSION_WORKSTATION_BINDING
web_bind_grace_seconds 300 KLANGKD_WEB_BIND_GRACE_SECONDS
trusted_proxy_cidrs 127.0.0.1,::1 KLANGKD_TRUSTED_PROXY_CIDRS

Server / network

Key Default Env var
listen 127.0.0.1 KLANGKD_LISTEN
port (unset) KLANGKD_PORT
egress_port 8995 KLANGKD_EGRESS_PORT
egress_listen 0.0.0.0 KLANGKD_EGRESS_LISTEN
tls_hostname KLANGKD_TLS_HOSTNAME
tls_issuer KLANGKD_TLS_ISSUER
acme_email KLANGKD_ACME_EMAIL
proxy_port (deprecated) KLANGKD_PROXY_PORT
socket <state_dir>/klangk.sock KLANGKD_SOCKET
caddy_admin_socket <state_dir>/caddy-admin.sock KLANGKD_CADDY_ADMIN_SOCKET
port_range_start 9000 KLANGKD_PORT_RANGE_START
cors_origins KLANGKD_CORS_ORIGINS
frontend_dir (in-package klangk/frontend) KLANGKD_FRONTEND_DIR
dns_servers KLANGKD_DNS_SERVERS
dns_search KLANGKD_DNS_SEARCH
hosting_hostname (auto-derived) KLANGKD_HOSTING_HOSTNAME
hosting_proto (auto-derived) KLANGKD_HOSTING_PROTO
hosting_base_path (auto-derived) KLANGKD_HOSTING_BASE_PATH
bridge_timeout_seconds KLANGKD_BRIDGE_TIMEOUT_SECONDS
idle_timeout_seconds 3600 KLANGKD_IDLE_TIMEOUT_SECONDS
log_level INFO KLANGKD_LOG_LEVEL
log_format text KLANGKD_LOG_FORMAT
log_file KLANGKD_LOG_FILE
log_file_max_bytes 0 KLANGKD_LOG_FILE_MAX_BYTES
log_file_rotate KLANGKD_LOG_FILE_ROTATE
log_file_backup_count 3 KLANGKD_LOG_FILE_BACKUP_COUNT
proxy_bin (auto-discovered) KLANGKD_PROXY_BIN
websocket_msg_size_max 16777216 KLANGKD_WEBSOCKET_MSG_SIZE_MAX
api_rate_limit 0 (off) KLANGKD_API_RATE_LIMIT

api_rate_limit caps /api/* requests per client IP (fixed 60s window, 429 + Retry-After), enforced in the backend process — it applies on every deployment shape (bare, managed Caddy, outer proxy). Default 0 = off (opt-in; a throttle on real traffic ships disabled — 300 is the documented example budget). Static assets, /ws upgrades, /hosted/*, and /health never count. The client IP resolves through KLANGKD_TRUSTED_PROXY_CIDRS — behind an outer proxy that isn't in that set (or with KLANGKD_REJECT_PROXY_HEADERS), every forwarded client shares the proxy's single bucket, so add the proxy there before relying on the limit.

idle_timeout_seconds is the deploy-wide default; a workspace can override it per-workspace (settings.idle_timeout — see Workspaces § Idle timeout).

Container / workspace

Key Default Env var
data_dir <state_dir>/data KLANGKD_DATA_DIR
state_dir $XDG_STATE_HOME/klangkd KLANGKD_STATE_DIR
config_dir $XDG_CONFIG_HOME/klangkd KLANGKD_CONFIG_DIR
customize_dir <config_dir>/custom KLANGKD_CUSTOMIZE_DIR
approved_ca_dir KLANGKD_TRUSTED_CA_DIR
features_enable (unset → manifest defaults) KLANGKD_FEATURES_ENABLE
image_name klangk-workspace KLANGKD_IMAGE_NAME
image_pull_policy never KLANGKD_IMAGE_PULL_POLICY
allowed_images KLANGKD_ALLOWED_IMAGES
allowed_mount_roots KLANGKD_ALLOWED_MOUNT_ROOTS
allow_autostart KLANGKD_ALLOW_AUTOSTART
allow_sudo true (ceiling only) KLANGKD_ALLOW_SUDO
per_handle_home false (ceiling only) KLANGKD_PER_HANDLE_HOME
classification_banner KLANGKD_CLASSIFICATION_BANNER
container_subnets (auto-derived) KLANGKD_CONTAINER_SUBNETS
userns keep-id:uid=1000,gid=1000 KLANGKD_USERNS
podman_bin podman KLANGKD_PODMAN_BIN
disable_tmux KLANGKD_DISABLE_TMUX
nix_enabled false KLANGKD_NIX_ENABLED
browser_delegate_enabled true KLANGKD_BROWSER_DELEGATE_ENABLED
netfilter_enabled true KLANGKD_NETFILTER_ENABLED
netfilter_default_domains (unset) KLANGKD_NETFILTER_DEFAULT_DOMAINS
egress_consent_retention_days 30 KLANGKD_EGRESS_CONSENT_RETENTION_DAYS
egress_consent_row_cap 2000 KLANGKD_EGRESS_CONSENT_ROW_CAP
container_cpu_limit 2.0 KLANGKD_CONTAINER_CPU_LIMIT
container_memory_limit 8g KLANGKD_CONTAINER_MEMORY_LIMIT
admission_memory_enabled false KLANGKD_ADMISSION_MEMORY_ENABLED
admission_memory_margin 1g KLANGKD_ADMISSION_MEMORY_MARGIN
max_running_workspaces_per_user 0 KLANGKD_MAX_RUNNING_WORKSPACES_PER_USER
volume_quota_per_workspace 0 KLANGKD_VOLUME_QUOTA_PER_WORKSPACE
memory_eviction_enabled true KLANGKD_MEMORY_EVICTION_ENABLED
memory_eviction_threshold_percent 10 KLANGKD_MEMORY_EVICTION_THRESHOLD_PERCENT
memory_eviction_recovery_percent 15 KLANGKD_MEMORY_EVICTION_RECOVERY_PERCENT
memory_eviction_sustain_polls 3 KLANGKD_MEMORY_EVICTION_SUSTAIN_POLLS
memory_eviction_poll_interval 10 (floored at 1) KLANGKD_MEMORY_EVICTION_POLL_INTERVAL
resource_watchdog_enabled true KLANGKD_RESOURCE_WATCHDOG_ENABLED
disk_watchdog_warn_percent 75 KLANGKD_DISK_WATCHDOG_WARN_PERCENT
disk_watchdog_critical_percent 90 KLANGKD_DISK_WATCHDOG_CRITICAL_PERCENT
disk_watchdog_paths KLANGKD_DISK_WATCHDOG_PATHS
memory_watchdog_enabled true KLANGKD_MEMORY_WATCHDOG_ENABLED
memory_watchdog_warn_percent 80 KLANGKD_MEMORY_WATCHDOG_WARN_PERCENT
memory_watchdog_critical_percent 90 KLANGKD_MEMORY_WATCHDOG_CRITICAL_PERCENT
cpu_watchdog_enabled true KLANGKD_CPU_WATCHDOG_ENABLED
cpu_watchdog_warn_percent 30 KLANGKD_CPU_WATCHDOG_WARN_PERCENT
cpu_watchdog_critical_percent 60 KLANGKD_CPU_WATCHDOG_CRITICAL_PERCENT
resource_watchdog_poll_interval 60 (floored at 1) KLANGKD_RESOURCE_WATCHDOG_POLL_INTERVAL
container_pids_limit 16384 KLANGKD_CONTAINER_PIDS_LIMIT
container_tmp_size 2g KLANGKD_CONTAINER_TMP_SIZE
container_events_retention_days 90 KLANGKD_CONTAINER_EVENTS_RETENTION_DAYS
container_events_row_cap 10000 KLANGKD_CONTAINER_EVENTS_ROW_CAP
audit_events_retention_days 365 KLANGKD_AUDIT_EVENTS_RETENTION_DAYS
audit_events_row_cap 100000 KLANGKD_AUDIT_EVENTS_ROW_CAP
audit_fail_closed false KLANGKD_AUDIT_FAIL_CLOSED
network_sidecar_image klangk-network-sidecar KLANGKD_NETWORK_SIDECAR_IMAGE
nix_seed (unset) KLANGKD_NIX_SEED__TYPE/__PATH
egress_consent_rate_limit 50 KLANGKD_EGRESS_CONSENT_RATE_LIMIT
egress_consent_timeout 120 KLANGKD_EGRESS_CONSENT_TIMEOUT
consent_decider_timeout 45 KLANGKD_CONSENT_DECIDER_TIMEOUT
container_restart_enabled false KLANGKD_CONTAINER_RESTART_ENABLED
container_restart_max_retries 5 KLANGKD_CONTAINER_RESTART_MAX_RETRIES
container_restart_backoff_seconds 5.0 KLANGKD_CONTAINER_RESTART_BACKOFF_SECONDS
quiesce_timeout 15.0 KLANGKD_QUIESCE_TIMEOUT
fips_mode false KLANGKD_FIPS_MODE
health_check_interval 30 KLANGKD_HEALTH_CHECK_INTERVAL
health_check_startup_grace 30 KLANGKD_HEALTH_CHECK_STARTUP_GRACE
health_check_timeout 10 KLANGKD_HEALTH_CHECK_TIMEOUT
hosted_ports_per_workspace 5 KLANGKD_HOSTED_PORTS_PER_WORKSPACE
test_mode KLANGKD_TEST_MODE
version_file KLANGKD_VERSION_FILE

approved_ca_dir is also settable through its issue-named env var KLANGKD_TRUSTED_CA_DIR (a pydantic validation_alias; the field-name form KLANGKD_APPROVED_CA_DIR works too, and KLANGKD_TRUSTED_CA_DIR wins when both are set). See Customizing a Deployment.

LLM

Key Default Env var Notes
llm-api-key KLANGKD_LLM_API_KEY Default API key for models without their own
llm-models KLANGKD_LLM_MODELS Model list (see LLM proxy)

OIDC

Key Default Env var
oidc_config KLANGKD_OIDC_CONFIG
oidc_login_hook KLANGKD_OIDC_LOGIN_HOOK
oidc_providers (inline only — not settable via env var)

Lifecycle hooks

Key Default Env var
workspace_created_hook KLANGKD_WORKSPACE_CREATED_HOOK

File path to a Python workspace-created hook (see Customizing a Deployment); fires on every creation path (create / import / duplicate) and may mutate the workspace and rewrite its ACL. Failures are logged, never fatal. Reloaded on SIGHUP.

SMTP / email

Key Default Env var
smtp_host KLANGKD_SMTP_HOST
smtp_port 587 KLANGKD_SMTP_PORT
smtp_user KLANGKD_SMTP_USER
smtp_password KLANGKD_SMTP_PASSWORD
smtp_from KLANGKD_SMTP_FROM
smtp_reply_to KLANGKD_SMTP_REPLY_TO
smtp_use_tls true KLANGKD_SMTP_USE_TLS
sendmail_path sendmail KLANGKD_SENDMAIL_PATH
email_templates_dir KLANGKD_EMAIL_TEMPLATES_DIR

Admin notifications

Key Default Env var
admin_notification_emails KLANGKD_ADMIN_NOTIFICATION_EMAILS
admin_notification_webhook_url KLANGKD_ADMIN_NOTIFICATION_WEBHOOK_URL
admin_notify_events every supported event KLANGKD_ADMIN_NOTIFY_EVENTS

Audit forwarding

Key Default Env var
audit_forward_url KLANGKD_AUDIT_FORWARD_URL
audit_forward_syslog KLANGKD_AUDIT_FORWARD_SYSLOG
audit_forward_header KLANGKD_AUDIT_FORWARD_HEADER

See Logging — built-in audit-record forwarding for target setup and failure behavior.

Key Default Env var
terms_url KLANGKD_TERMS_URL
privacy_url KLANGKD_PRIVACY_URL
aup_url KLANGKD_AUP_URL
support_url KLANGKD_SUPPORT_URL
support_email KLANGKD_SUPPORT_EMAIL

Branding / UI

Key Default Env var
product_name Klangk KLANGKD_PRODUCT_NAME
logo_url KLANGKD_LOGO_URL
brand_color #E65100 KLANGKD_BRAND_COLOR
login_banner KLANGKD_LOGIN_BANNER
login_banner_title KLANGKD_LOGIN_BANNER_TITLE
login_banner_every_visit false KLANGKD_LOGIN_BANNER_EVERY_VISIT
terminal_banner KLANGKD_TERMINAL_BANNER

SSL / certs

Custom CA certificates are not a config key — drop .pem/.crt files into <KLANGKD_CUSTOMIZE_DIR>/certs/ and both the backend and workspace containers will trust them. There is no ssl_cert_dir setting.

File upload

Key Default Env var
file_upload_size_max 524288000 KLANGKD_FILE_UPLOAD_SIZE_MAX
import_max_uncompressed_mb (unset) KLANGKD_IMPORT_MAX_UNCOMPRESSED_MB

file_upload_size_max bounds the compressed upload. Workspace imports are bounded on the decompressed side too (#3284): the archive's workspace.json member is capped at 1 MB, and the home/ tree is pre-scanned and refused with 413 when it would exceed the free space on the workspace volume (a 2 GB reserve is kept) or import_max_uncompressed_mb. A legitimate multi-gigabyte home import succeeds whenever the volume has room. The free-space check runs per import: two imports started at the same time can together consume more than either alone would pass, so the reserve is a working margin rather than a hard guarantee against a full disk. The pre-scan must finish within 30 seconds; an archive that takes longer to list (because it expands to hundreds of gigabytes of members) is refused as an invalid archive.

Feature configuration

The features_config block supplies values for feature-declared keys (the ones the build emits into features.json) from the config file — a second source alongside env. See the dedicated section above for the precedence rule and file:/cmd: handling. There is no KLANGK_* env var for the whole block (env still wins per key — set the individual KLANGKWS_FEATURE_* var to override a single value); the block is the config-file substrate for durable deploy values.

Key Default Env var
features_config (unset) _(none — per-key KLANGKWS_FEATURE_\* env vars are the per-value override)_