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):
- Environment variables (
KLANGK_*) — override everything - Config file — the YAML file passed to
--config - 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:
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", not8080. (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 thefile: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_PORTonly: unset ⇒ headless (container-egress only — no browser UI); set ⇒ full (browser UI + API + hosted apps).KLANGKD_AUTH_MODESdoes not change which template renders. - The one gate (
noneon non-loopbackKLANGKD_LISTEN) is enforced byenforce_no_auth_bind_safety()at boot — refused unlessKLANGKD_ALLOW_INSECURE_NO_AUTH=1is 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.
Legal / support links¶
| 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)_ |