Skip to content

Config Reference

A copy-paste-ready annotated drakkar.yaml showing every configurable field. Each line carries a one-sentence explanation and the matching DK_* environment-variable override. The πŸ“š link beside each section header takes you to the deep tables and prose in Configuration.

How to read this page

  • Pick only the sections you need – every field has a sane default, so an empty drakkar.yaml is a valid config.
  • Comments mark reasonable values in [brackets] so you know what’s typical vs. extreme.
  • Every field can be overridden by an env var. The pattern is DK_<SECTION>__<FIELD> (double underscore for nesting). See Configuration Loading for the full precedence order.
  • DK_CONFIG=/path/to/drakkar.yaml selects which YAML file to load (it’s not a config field itself).

Worker identity

πŸ“š Deep details

Top-level fields that identify this worker in logs, metrics, and the operator UI.

# Name of the env var that holds this worker's display name.
# Empty / unset env var β†’ falls back to "drakkar-<hex>".
worker_name_env: WORKER_ID         # env: DK_WORKER_NAME_ENV  Β· reasonable: WORKER_ID, HOSTNAME, K8S_POD_NAME

# Logical cluster name. Workers sharing this name are grouped in the operator UI
# and can cross-trace messages.
cluster_name: ''                   # env: DK_CLUSTER_NAME  Β· reasonable: search-cluster, prod-east, ''

# Env var that holds the cluster name. If set and non-empty, overrides `cluster_name`.
# Useful when the cluster name comes from the deployment platform (k8s, ECS).
cluster_name_env: ''               # env: DK_CLUSTER_NAME_ENV  Β· reasonable: K8S_NAMESPACE, DEPLOY_ENV, ''

Kafka source (kafka:)

πŸ“š Deep details Β· Staggered startup

The Kafka consumer that reads input messages. brokers doubles as the fallback for sink and DLQ brokers when those are left empty.

kafka:
  brokers: localhost:9092          # bootstrap servers, comma-separated. env: DK_KAFKA__BROKERS  Β· reasonable: kafka-1:9092,kafka-2:9092
  source_topic: input-events       # topic to consume from. env: DK_KAFKA__SOURCE_TOPIC
  consumer_group: drakkar-workers  # consumer-group ID; workers sharing it split partitions. env: DK_KAFKA__CONSUMER_GROUP

  max_poll_records: 100            # messages per poll batch; ↑ throughput / ↓ latency. env: DK_KAFKA__MAX_POLL_RECORDS  Β· reasonable: 50–500
  max_poll_interval_ms: 300000     # max ms between polls before broker kicks us out. env: DK_KAFKA__MAX_POLL_INTERVAL_MS  Β· raise for slow tasks
  session_timeout_ms: 45000        # group-membership heartbeat window. env: DK_KAFKA__SESSION_TIMEOUT_MS
  heartbeat_interval_ms: 3000      # heartbeat frequency; should be ≀ session_timeout_ms / 3. env: DK_KAFKA__HEARTBEAT_INTERVAL_MS

  # Policy for messages whose value fails input_model parsing. env: DK_KAFKA__ON_PARSE_ERROR
  #   skip  β€” message reaches arrange() with payload=None and msg.parse_error set (default)
  #   dlq   β€” message is excluded from arrange() and written to the DLQ topic as a
  #           ParseFailurePayload; the offset commits only after the DLQ write is confirmed
  #   raise β€” fail fast: MessageParseError stops the partition processor (schema-broken deploys)
  on_parse_error: skip

  # Transport security. Default PLAINTEXT emits no client properties at all, so a worker
  # that configures nothing here connects exactly as it always did. Kafka sinks and the DLQ
  # inherit this block whenever their own `brokers` field is empty.
  security:
    protocol: PLAINTEXT            # PLAINTEXT | SSL | SASL_PLAINTEXT | SASL_SSL. env: DK_KAFKA__SECURITY__PROTOCOL
    sasl_mechanism: null           # PLAIN | SCRAM-SHA-256 | SCRAM-SHA-512 | GSSAPI | OAUTHBEARER.
                                   #   Required for SASL_* protocols, rejected for the others.
                                   #   env: DK_KAFKA__SECURITY__SASL_MECHANISM
    sasl_username: ''              # env: DK_KAFKA__SECURITY__SASL_USERNAME
    sasl_password: ''              # prefer the env override over a YAML literal.
                                   #   env: DK_KAFKA__SECURITY__SASL_PASSWORD
    ssl_ca_location: ''            # PEM CA bundle; empty uses the system trust store.
                                   #   env: DK_KAFKA__SECURITY__SSL_CA_LOCATION
    ssl_certificate_location: ''   # client cert (mutual TLS). env: DK_KAFKA__SECURITY__SSL_CERTIFICATE_LOCATION
    ssl_key_location: ''           # client key (mutual TLS); requires ssl_certificate_location.
                                   #   env: DK_KAFKA__SECURITY__SSL_KEY_LOCATION
    ssl_key_password: ''           # passphrase for an encrypted client key.
                                   #   env: DK_KAFKA__SECURITY__SSL_KEY_PASSWORD
    ssl_endpoint_identification_algorithm: null  # unset = librdkafka default ("https", verification on).
                                   #   Set to "none" ONLY for internal CAs without matching SANs.

  # Escape hatch: raw librdkafka properties, merged AFTER `security` so they win.
  # For options the typed block does not model. These four keys are rejected at startup β€”
  # each one backs a delivery invariant: enable.auto.commit, partition.assignment.strategy,
  # group.id, bootstrap.servers.
  client_config: {}                # env: DK_KAFKA__CLIENT_CONFIG (JSON object)

  # Kafka-UI deep-link integration: when both are set, the operator UI shows a clickable
  # icon next to every <partition:offset>. Both empty = feature disabled silently.
  ui_url: ''                       # env: DK_KAFKA__UI_URL  Β· example: http://kafka-ui:8080
  ui_cluster_name: ''              # cluster name registered in Kafka-UI. env: DK_KAFKA__UI_CLUSTER_NAME

  # Rolling-deploy alignment: serialize fleet-wide consumer-group rebalances.
  startup_align_enabled: true      # disable for snappy single-process dev. env: DK_KAFKA__STARTUP_ALIGN_ENABLED
  startup_min_wait_seconds: 4.0    # min sleep before alignment (buffer for slow init). env: DK_KAFKA__STARTUP_MIN_WAIT_SECONDS
  startup_align_interval_seconds: 10  # wake at next time%interval==0 boundary. env: DK_KAFKA__STARTUP_ALIGN_INTERVAL_SECONDS

Executor pool (executor:)

πŸ“š Deep details Β· Backpressure formula

Subprocess pool that runs user binaries. Tune max_executors and task_timeout_seconds first; everything else has good defaults.

executor:
  binary_path: null                # default binary for tasks. env: DK_EXECUTOR__BINARY_PATH  Β· null = ExecutorTask must set its own
  max_executors: 4                 # concurrent subprocesses. env: DK_EXECUTOR__MAX_EXECUTORS  Β· reasonable: 2–32
  task_timeout_seconds: 120        # wall-clock cap per task. env: DK_EXECUTOR__TASK_TIMEOUT_SECONDS  Β· reasonable: 30–600
  max_stdout_bytes: 0              # stdout bytes retained per task (0 = unlimited). env: DK_EXECUTOR__MAX_STDOUT_BYTES
  max_stderr_bytes: 0              # stderr bytes retained per task (0 = unlimited). env: DK_EXECUTOR__MAX_STDERR_BYTES
  window_size: 100                 # max messages collected per arrange() window. env: DK_EXECUTOR__WINDOW_SIZE
  max_retries: 3                   # retries per failed task (0 = no retries). env: DK_EXECUTOR__MAX_RETRIES
  drain_timeout_seconds: 30        # graceful-shutdown wait for in-flight tasks. env: DK_EXECUTOR__DRAIN_TIMEOUT_SECONDS

  # Backpressure: pause Kafka consumption when in-flight messages reach
  # max_executors * high_multiplier; resume when down to max_executors * low_multiplier.
  backpressure_high_multiplier: 32 # env: DK_EXECUTOR__BACKPRESSURE_HIGH_MULTIPLIER  Β· default 32 = pause at 4*32=128 queued
  backpressure_low_multiplier: 4   # env: DK_EXECUTOR__BACKPRESSURE_LOW_MULTIPLIER  Β· default 4 = resume at max(1,4*4)=16 queued

  # Subprocess environment.
  env: {}                          # env vars passed to all subprocesses. env: DK_EXECUTOR__ENV (JSON)
  env_inherit_parent: true         # pass parent process env (filtered). env: DK_EXECUTOR__ENV_INHERIT_PARENT
  env_inherit_deny:                # fnmatch patterns NOT inherited (case-insensitive). env: DK_EXECUTOR__ENV_INHERIT_DENY (JSON list)
    - 'DK_*'                       # framework internals
    - '*PASSWORD*'
    - '*PASSWD*'
    - '*SECRET*'
    - '*TOKEN*'
    - '*_KEY'
    - '*_DSN'
    - '*CREDENTIAL*'
    - '*SALT*'

Sinks (sinks:)

πŸ“š Deep details Β· Sinks page

Where processed results are delivered. Each sink type maps instance names β†’ config, so you can have multiple instances of the same type writing to different destinations. All instance keys (e.g. results, analytics) are user-chosen.

Kafka sink

πŸ“š Deep details

sinks:
  kafka:
    results:                       # instance name (free-form, referenced from handler code)
      topic: search-results        # required. env: DK_SINKS__KAFKA__RESULTS__TOPIC
      brokers: ''                  # empty = inherit kafka.brokers AND kafka.security.
                                   #   env: DK_SINKS__KAFKA__RESULTS__BROKERS
      ui_url: ''                   # link to Kafka-UI/Kowl in the UI dashboard. env: DK_SINKS__KAFKA__RESULTS__UI_URL
      security: {}                 # only consulted when `brokers` is set above; same fields as
                                   #   kafka.security. Leaving brokers empty is the usual choice.
      client_config: {}            # raw librdkafka overrides for this sink
      flush_timeout_seconds: 30.0  # bound on the per-batch producer flush. env: DK_SINKS__KAFKA__RESULTS__FLUSH_TIMEOUT_SECONDS
                                   #   reasonable: 10-60; a last resort, not a latency target

PostgreSQL sink

πŸ“š Deep details

sinks:
  postgres:
    main-db:
      dsn: postgresql://user:pass@db:5432/myapp  # required. env: DK_SINKS__POSTGRES__MAIN_DB__DSN
      pool_min: 2                  # min connections in asyncpg pool. env: DK_SINKS__POSTGRES__MAIN_DB__POOL_MIN  Β· reasonable: 1–10
      pool_max: 10                 # max connections. env: DK_SINKS__POSTGRES__MAIN_DB__POOL_MAX  Β· reasonable: 5–50
      ui_url: ''                   # pgAdmin / Adminer URL. env: DK_SINKS__POSTGRES__MAIN_DB__UI_URL
      statements:                  # optional. Operator-authored SQL by name
        claim_job: |               # env: DK_SINKS__POSTGRES__MAIN_DB__STATEMENTS__CLAIM_JOB
          UPDATE jobs SET status = :status WHERE id = :id AND status = 'pending'

Statement names must match ^[a-z_][a-z0-9_]*$; placeholders are written :name. Malformed SQL fails at startup, schema errors at delivery. See Sink Write Operations.

MongoDB sink

πŸ“š Deep details

sinks:
  mongo:
    logs:
      uri: mongodb://mongo:27017   # required. env: DK_SINKS__MONGO__LOGS__URI
      database: app_logs           # required. env: DK_SINKS__MONGO__LOGS__DATABASE
      ui_url: ''                   # Mongo Express URL. env: DK_SINKS__MONGO__LOGS__UI_URL
      statements:                  # optional. Operator-authored MQL by name
        record_attempt:            # env: DK_SINKS__MONGO__LOGS__STATEMENTS__RECORD_ATTEMPT__COLLECTION
          collection: jobs
          op: update_one           # one of: update_one, update_many, upsert, delete_one, delete_many
          filter: { _id: ":id" }   # required, and never empty
          update:                  # required for the update ops, absent for the deletes
            $set: { last_seen: ":now" }
            $inc: { attempts: 1 }

Statement names must match ^[a-z_][a-z0-9_]*$. Values bind through ":name" placeholders, whole values only and type-preserved; "::name" escapes a literal leading colon. $where and $function are rejected at startup at any depth, including inside aggregation-pipeline stages. Statements are not verified against the live database. See Sink Write Operations.

HTTP sink

πŸ“š Deep details

Cloud metadata endpoints (169.254.169.254, metadata.google.internal, etc.) are rejected at config load to prevent IAM-credential leaks via misconfiguration.

sinks:
  http:
    webhook:
      url: https://api.example.com/webhook  # required, http(s) scheme. env: DK_SINKS__HTTP__WEBHOOK__URL
      method: POST                 # env: DK_SINKS__HTTP__WEBHOOK__METHOD
      timeout_seconds: 30          # request timeout. env: DK_SINKS__HTTP__WEBHOOK__TIMEOUT_SECONDS
      max_retries: 3               # retries per failed request. env: DK_SINKS__HTTP__WEBHOOK__MAX_RETRIES
      headers: {}                  # extra request headers. env: DK_SINKS__HTTP__WEBHOOK__HEADERS (JSON)
      encoding: json               # body format: json | form | multipart. env: DK_SINKS__HTTP__WEBHOOK__ENCODING
      ui_url: ''                   # env: DK_SINKS__HTTP__WEBHOOK__UI_URL

Redis sink

πŸ“š Deep details

sinks:
  redis:
    cache:
      url: redis://localhost:6379/0  # env: DK_SINKS__REDIS__CACHE__URL
      key_prefix: ''               # prepended to every key, e.g. "result:". env: DK_SINKS__REDIS__CACHE__KEY_PREFIX
      ui_url: ''                   # RedisInsight URL. env: DK_SINKS__REDIS__CACHE__UI_URL
      scripts:                     # optional. Operator-authored Lua by name
        push_and_cap: |            # env: DK_SINKS__REDIS__CACHE__SCRIPTS__PUSH_AND_CAP
          redis.call('LPUSH', KEYS[1], ARGV[1])
          redis.call('LTRIM', KEYS[1], 0, tonumber(ARGV[2]) - 1)

Script names must match ^[a-z_][a-z0-9_]*$; values reach the script as KEYS and ARGV, never interpolated. The Lua is not parsed at startup and not checked against a live server, so a broken script fails at delivery. See Sink Write Operations.

Filesystem sink

πŸ“š Deep details

sinks:
  filesystem:
    archive:
      base_path: /data/archive     # required, payload paths are resolved under this. env: DK_SINKS__FILESYSTEM__ARCHIVE__BASE_PATH
      ui_url: ''                   # env: DK_SINKS__FILESYSTEM__ARCHIVE__UI_URL

Custom sinks (plugins)

πŸ“š Deep details Β· Plugin API

Instances of sink types registered by plugin packages via [project.entry-points."drakkar.sinks"]. The leaf config dict is plugin-defined and passed verbatim to the sink class constructor; an unregistered type name fails at startup.

sinks:
  custom:
    my_custom_type:                # entry-point-registered sink type name
      audit_trail_out:             # instance name (free-form, referenced from handler code)
        endpoint: https://audit.internal.example.com   # plugin-defined fields β€” Drakkar passes
        buffer_size: 500                               #   the dict through unvalidated

Delivery timeout (shared default)

πŸ“š Deep details

sinks:
  delivery_timeout_seconds: 30.0   # budget for one deliver() and one close(). env: DK_SINKS__DELIVERY_TIMEOUT_SECONDS  Β· reasonable: 10–120

Circuit breaker (shared default)

πŸ“š Deep details Β· State machine

Applies uniformly to every sink instance. Per-sink overrides are not supported in v1 – if one sink needs different thresholds, adjust the global default.

sinks:
  circuit_breaker:
    failure_threshold: 5           # consecutive terminal failures to trip. env: DK_SINKS__CIRCUIT_BREAKER__FAILURE_THRESHOLD  Β· reasonable: 3–10
    cooldown_seconds: 30.0         # seconds open before half-open probe. env: DK_SINKS__CIRCUIT_BREAKER__COOLDOWN_SECONDS  Β· reasonable: 10–120

Dead letter queue (dlq:)

πŸ“š Deep details Β· DLQ behavior

Failed sink deliveries (after retries are exhausted, or when a circuit breaker is open) are written to this Kafka topic.

dlq:
  topic: ''                        # empty = auto-derive "{source_topic}_dlq". env: DK_DLQ__TOPIC
  brokers: ''                      # empty = inherit kafka.brokers AND kafka.security. env: DK_DLQ__BROKERS
  security: {}                     # only consulted when `brokers` is set above; same fields as
                                   #   kafka.security (env: DK_DLQ__SECURITY__<FIELD>).
                                   #   Leaving brokers empty is the usual choice.
  client_config: {}                # raw librdkafka overrides for the DLQ producer, merged after
                                   #   security; only consulted when `brokers` is set

  # Strategy when the DLQ write itself fails (payloads have nowhere safe to go):
  #   drop  β€” log CRITICAL + tick drakkar_dlq_dropped_payloads_total, commit the offset,
  #           keep the pipeline moving; the payloads are lost (default)
  #   stall β€” leave the offset uncommitted and pause the partition; messages are
  #           redelivered after restart/rebalance (no loss, at the cost of lag)
  flush_timeout_seconds: 30.0      # bound on the per-write producer flush. env: DK_DLQ__FLUSH_TIMEOUT_SECONDS
  on_send_failure: drop            # env: DK_DLQ__ON_SEND_FAILURE

Metrics (metrics:)

πŸ“š Deep details Β· Observability

Prometheus scrape endpoint.

metrics:
  enabled: true                    # env: DK_METRICS__ENABLED
  port: 9090                       # 1–65535. env: DK_METRICS__PORT
  task_label_histograms: []        # task label keys observed into the
                                   # drakkar_task_label_value histogram, one
                                   # time series per key, e.g. [file_size_bytes]
                                   # env: DK_METRICS__TASK_LABEL_HISTOGRAMS

Throughput (throughput:)

πŸ“š Throughput Β· Observability

Opt-in task cost tracking: a numeric label becomes per-task speed and sliding-window throughput.

throughput:
  cost_label: ""     # task label key holding the numeric cost; "" disables.
                     # env: DK_THROUGHPUT__COST_LABEL
  min_cost: 0.0      # smallest cost worth counting; smaller tasks carry no
                     # speed and enter no aggregate.
                     # env: DK_THROUGHPUT__MIN_COST

Runtime health (runtime_health:)

πŸ“š Runtime health Β· Observability

Event-loop lag monitoring and stall introspection.

runtime_health:
  enabled: true                    # env: DK_RUNTIME_HEALTH__ENABLED
  tick_seconds: 0.25               # heartbeat interval. env: DK_RUNTIME_HEALTH__TICK_SECONDS
  warn_lag_seconds: 0.1            # lag above this = degraded. env: DK_RUNTIME_HEALTH__WARN_LAG_SECONDS
  stall_seconds: 1.0               # heartbeat silence above this = stalled + stack capture.
                                   # env: DK_RUNTIME_HEALTH__STALL_SECONDS
  max_stall_stacks: 10             # distinct stacks kept per stall. env: DK_RUNTIME_HEALTH__MAX_STALL_STACKS
  sample_interval_seconds: 10.0    # recorder history sample cadence. env: DK_RUNTIME_HEALTH__SAMPLE_INTERVAL_SECONDS
  history_window_seconds: 900      # in-memory sparkline window. env: DK_RUNTIME_HEALTH__HISTORY_WINDOW_SECONDS
  episode_max_seconds: 300.0       # max length of one lag episode before it flushes.
                                   # env: DK_RUNTIME_HEALTH__EPISODE_MAX_SECONDS
  probe_interval_seconds: 0.0      # opt-in stack probe cadence; 0 disables.
                                   # env: DK_RUNTIME_HEALTH__PROBE_INTERVAL_SECONDS

Blocking I/O pool (io:)

πŸ“š Threads & Pools Β· Offloading CPU-bound hook work

asyncio’s default to_thread executor: every blocking call the handler (or the framework) sends through asyncio.to_thread shares this one process-wide pool.

io:
  max_threads: 0     # 0 = keep Python's default, min(32, cpu_count + 4).
                     # Set explicitly when blocking-I/O fan-out is capped by
                     # the pool (e.g. many-core hosts reading slow storage).
                     # env: DK_IO__MAX_THREADS

Offload pool (offload:)

πŸ“š Offloading CPU-bound hook work Β· Runtime health

Thread pool behind handler.offload() β€” CPU-bound hook computations moved off the event loop so the worker stays responsive.

offload:
  max_threads: 0                   # concurrent offloaded computations before queueing.
                                   # 0 = auto: ceil(executor.max_executors / 4), min 2.
                                   # env: DK_OFFLOAD__MAX_THREADS

Logging (logging:)

πŸ“š Deep details

Structured logging via structlog.

logging:
  level: INFO                      # DEBUG | INFO | WARNING | ERROR | CRITICAL. env: DK_LOGGING__LEVEL
  format: json                     # "json" for prod, "console" for dev. env: DK_LOGGING__FORMAT
  output: stderr                   # stderr | stdout | file path. env: DK_LOGGING__OUTPUT
                                   # File paths support {worker_id}, {cluster_name} templating
                                   # e.g. /var/log/drakkar/{worker_id}.log

UI / Flight Recorder (ui:)

πŸ“š Deep details Β· Authentication Β· Operator UI

The largest section. Provides the operator web UI, a SQLite-backed event log (ui.recorder.*), drakkar-ui bundle fetching (ui.release.*), WebSocket live streaming, worker autodiscovery, and Prometheus deep-links.

ui:
  # --- Server ---
  enabled: true                    # master switch for the whole UI feature. env: DK_UI__ENABLED
  host: 127.0.0.1                  # bind address. Use 0.0.0.0 for non-loopback. env: DK_UI__HOST
  port: 8080                       # 1–65535. env: DK_UI__PORT
  public_url: ''                   # external URL advertised to peers (LB / ingress). env: DK_UI__PUBLIC_URL
  workers_offline_after_seconds: 30  # peer with no heartbeat newer than this shows offline in the
                                   # workers list; use >= 2-3x the fleet's largest recorder
                                   # state_sync_interval_seconds. env: DK_UI__WORKERS_OFFLINE_AFTER_SECONDS

  # --- Auth (opt-in by default; most endpoints are read-only) ---
  auth_token: ''                   # bearer token; empty = unauthenticated + startup warning. env: DK_UI__AUTH_TOKEN
                                   # Generate: python -c "import secrets; print(secrets.token_urlsafe(32))"
  allowed_ws_origins: []           # WebSocket Origin allowlist. env: DK_UI__ALLOWED_WS_ORIGINS (JSON list)

  # --- Side-effecting endpoints (gated independently of auth_token) ---
  probe_enabled: true              # POST /api/v1/debug/probe; runs caller bytes through the live handler
                                   # and takes executor slots. false = 403. env: DK_UI__PROBE_ENABLED
  merge_enabled: true              # POST /api/v1/debug/merge; writes merged-<ts>.db into recorder.db_dir,
                                   # never reclaimed. false = 403. env: DK_UI__MERGE_ENABLED
  kafka_read_enabled: true         # GET /api/v1/debug/kafka/*; ad-hoc reads of the configured topics
                                   # (source/dlq/sink aliases only). false = 403. env: DK_UI__KAFKA_READ_ENABLED

  # --- Timed consume pause (Live page; OPT-IN β€” pausing stops message intake) ---
  consume_pause:
    enabled: false                 # serve the pause API + Live-page control. env: DK_UI__CONSUME_PAUSE__ENABLED
    durations_seconds: [15, 60, 300, 900]  # preset buttons; API accepts any 1..3600s.
                                   # env: DK_UI__CONSUME_PAUSE__DURATIONS_SECONDS (JSON list)

  # --- Deployment metadata ---
  expose_env_vars: []              # env vars captured into worker_config table. env: DK_UI__EXPOSE_ENV_VARS (JSON list)
                                   # e.g. ['GIT_SHA', 'DEPLOY_ENV', 'K8S_POD_NAME']

  # --- Presentation ---
  max_rows: 5000                   # max rows returned by UI list endpoints. env: DK_UI__MAX_ROWS

  # --- Duration thresholds (noise filters) ---
  log_min_duration_ms: 500         # min ms to log slow_task_completed/failed. env: DK_UI__LOG_MIN_DURATION_MS
  ws_min_duration_ms: 500          # min ms to broadcast over WebSocket. env: DK_UI__WS_MIN_DURATION_MS

  # --- Prometheus deep-links in the UI ---
  prometheus_url: ''               # e.g. http://prometheus:9090. env: DK_UI__PROMETHEUS_URL
  prometheus_rate_interval: 5m     # rate() interval used in dashboard PromQL. env: DK_UI__PROMETHEUS_RATE_INTERVAL
  prometheus_worker_label: ''      # PromQL label for worker-scoped queries. env: DK_UI__PROMETHEUS_WORKER_LABEL
                                   # Supports {worker_id}, {cluster_name}, {metrics_port}, {debug_port}
                                   # e.g. 'worker_id="{worker_id}"'
  prometheus_cluster_label: ''     # PromQL label for cluster-wide queries. env: DK_UI__PROMETHEUS_CLUSTER_LABEL
                                   # e.g. 'cluster="{cluster_name}"'

  # --- Custom links shown in the dashboard nav ---
  custom_links: []                 # env: DK_UI__CUSTOM_LINKS (JSON list)
                                   # Each entry: {name: "...", url: "..."}; url supports {worker_id} etc.

  # --- Named URL bases for probe-details link templates (see ui-enrichment.md) ---
  link_bases: {}                   # env: DK_UI__LINK_BASES (JSON object)
                                   # e.g. {jira: 'https://jira.internal.example.com'}; a template base
                                   # missing here logs a startup warning and renders as plain text.

  # --- Deployment-provided custom cell renderers module (see ui-enrichment.md) ---
  custom_renderers_path: ''        # path to a JS module served at /api/v1/ui/renderers.js. env: DK_UI__CUSTOM_RENDERERS_PATH
                                   # '' = off. Must exist at boot when set. Referenced from
                                   # probe_field(view='custom', renderer=...) / Column(renderer=...).

  # --- Flight recorder (persistence; all flags require non-empty db_dir) ---
  recorder:
    db_dir: /tmp                     # SQLite directory. Empty = no disk persistence. env: DK_UI__RECORDER__DB_DIR
                                     # Use shared FS (NFS, EFS) for cross-worker autodiscovery
    store_events: true               # write per-message events. env: DK_UI__RECORDER__STORE_EVENTS
    store_config: true               # write worker config (enables autodiscovery). env: DK_UI__RECORDER__STORE_CONFIG
    store_state: true                # periodic worker-state snapshots. env: DK_UI__RECORDER__STORE_STATE
    state_sync_interval_seconds: 10  # snapshot frequency. env: DK_UI__RECORDER__STATE_SYNC_INTERVAL_SECONDS

    # --- Database rotation & archiving (see docs/local-databases.md#archiving) ---
    rotation_interval_hours: 1       # how often to rotate the SQLite file; 1 = 1 hour. env: DK_UI__RECORDER__ROTATION_INTERVAL_HOURS
    archive_enabled: true            # merge rotated-out files into windowed .db.gz archives instead of leaking them. env: DK_UI__RECORDER__ARCHIVE_ENABLED
    archive_window_hours: 24         # one archive per cluster per window; must be >= rotation_interval_hours. env: DK_UI__RECORDER__ARCHIVE_WINDOW_HOURS
    archive_retention_days: 30       # delete archives older than this; 0 = keep forever. Must be >= 2x the window, in days. env: DK_UI__RECORDER__ARCHIVE_RETENTION_DAYS

    # --- Databases-page stats cache (see docs/local-databases.md#the-databases-page-stats-cache) ---
    dbstats_warm_interval_seconds: 60 # background sweep filling the .dbstats cache + purging deleted files.
                                      # env: DK_UI__RECORDER__DBSTATS_WARM_INTERVAL_SECONDS
    dbstats_inline_scan_limit: 4      # max cold files one /api/v1/debug/databases request scans inline;
                                      # the rest return stats_pending. env: DK_UI__RECORDER__DBSTATS_INLINE_SCAN_LIMIT

    # --- Output (stdout/stderr) capture ---
    store_output: true               # include subprocess output in events. env: DK_UI__RECORDER__STORE_OUTPUT
    store_stdin: false               # store each task's stdin (capped) in task_started
                                     # metadata; failures always store it. env: DK_UI__RECORDER__STORE_STDIN
    stdin_max_bytes: 65536           # byte cap for stored stdin, 0 = unlimited.
                                     # env: DK_UI__RECORDER__STDIN_MAX_BYTES
    flush_interval_seconds: 5        # in-memory buffer β†’ SQLite cadence. env: DK_UI__RECORDER__FLUSH_INTERVAL_SECONDS
    max_buffer: 50000                # ring-buffer capacity. env: DK_UI__RECORDER__MAX_BUFFER
    max_flush_retries: 3             # retries on transient SQLite errors. env: DK_UI__RECORDER__MAX_FLUSH_RETRIES
    event_min_duration_ms: 0         # min ms to persist to SQLite (0 = persist all). env: DK_UI__RECORDER__EVENT_MIN_DURATION_MS
    output_min_duration_ms: 500      # min ms to include stdout/stderr in event. env: DK_UI__RECORDER__OUTPUT_MIN_DURATION_MS

    # --- Handler annotations (see annotations.md; 0 disables a byte cap) ---
    annotations_enabled: true        # record self.annotate(...) rows in the events table. env: DK_UI__RECORDER__ANNOTATIONS_ENABLED
    annotation_max_bytes: 16384      # cap per annotation payload; oversize records drop whole.
                                     # env: DK_UI__RECORDER__ANNOTATION_MAX_BYTES
    annotation_max_bytes_per_call: 262144  # total annotation bytes one hook invocation may add.
                                     # env: DK_UI__RECORDER__ANNOTATION_MAX_BYTES_PER_CALL
    annotation_log_max_bytes: 2048   # cap on the payload copy in the drop-warning log line.
                                     # env: DK_UI__RECORDER__ANNOTATION_LOG_MAX_BYTES

  # --- drakkar-ui bundle fetching (never fatal; with nothing cached the worker runs API-only) ---
  release:
    enabled: true                    # resolve + serve the drakkar-ui bundle. env: DK_UI__RELEASE__ENABLED
    repo: wlame/drakkar-ui           # "owner/name" GitHub repo publishing UI bundles. env: DK_UI__RELEASE__REPO
    pinned_version: ''               # known-good UI release tag (e.g. v1.2.0); '' = unpinned. env: DK_UI__RELEASE__PINNED_VERSION
    cache_dir: ''                    # bundle cache root; '' = $XDG_CACHE_HOME/drakkar/ui. env: DK_UI__RELEASE__CACHE_DIR
    check_update: true               # resolve the latest release tag on startup. env: DK_UI__RELEASE__CHECK_UPDATE

  # --- Message Probe user-details write caps (see probe-user-details.md) ---
  probe_details:
    max_writes: 10000                # max probe.set/append/update calls per probe run; the first write
                                     # past the cap records one ProbeError, further writes drop silently.
                                     # env: DK_UI__PROBE_DETAILS__MAX_WRITES
    max_total_bytes: 5000000         # total serialized bytes of probe-details writes per run; past it,
                                     # writes drop like max_writes. env: DK_UI__PROBE_DETAILS__MAX_TOTAL_BYTES

  # --- Live timeline tuning: history depth, bar color rules, label roles (see ui-timeline.md) ---
  timeline:
    history_factor: 100              # depth = history_factor x executor.max_executors (x8 if no pool), capped at 100000. env: DK_UI__TIMELINE__HISTORY_FACTOR
    max_age_minutes: 60              # 1-1440; oldest task age the timeline shows. env: DK_UI__TIMELINE__MAX_AGE_MINUTES
    color_rules: []                  # first-match-wins bar-coloring rules; max 50. env: DK_UI__TIMELINE__COLOR_RULES (JSON list)
                                     # e.g. [{name: failed, when: {field: status, op: eq, value: failed}, color: red}]
    labels:                          # which task label fills each special role; '' = unbound
      tag: ''                        # env: DK_UI__TIMELINE__LABELS__TAG
      caption: ''                    # env: DK_UI__TIMELINE__LABELS__CAPTION
      highlight: ''                  # env: DK_UI__TIMELINE__LABELS__HIGHLIGHT
      filter: ''                     # env: DK_UI__TIMELINE__LABELS__FILTER
      marker: ''                     # env: DK_UI__TIMELINE__LABELS__MARKER

Cache (cache:)

πŸ“š Deep details Β· Peer Sync Β· Cache page

Optional handler-accessible key/value cache. Disabled by default.

cache:
  enabled: false                   # master switch; false = no-op stub. env: DK_CACHE__ENABLED
  db_dir: ''                       # SQLite dir; empty = falls back to ui.recorder.db_dir. env: DK_CACHE__DB_DIR
  flush_interval_seconds: 3.0      # write-behind flush cadence. env: DK_CACHE__FLUSH_INTERVAL_SECONDS
  cleanup_interval_seconds: 60.0   # expired-row cleanup cadence. env: DK_CACHE__CLEANUP_INTERVAL_SECONDS
  max_memory_entries: 10000        # in-memory LRU cap; null = unbounded (warns). env: DK_CACHE__MAX_MEMORY_ENTRIES

  # --- Cross-worker peer sync (LWW merge by updated_at_ms) ---
  peer_sync:
    enabled: true                  # env: DK_CACHE__PEER_SYNC__ENABLED
    interval_seconds: 30.0         # peer-sync cycle cadence. env: DK_CACHE__PEER_SYNC__INTERVAL_SECONDS
    batch_size: 500                # max rows pulled per peer per cycle. env: DK_CACHE__PEER_SYNC__BATCH_SIZE
    timeout_seconds: 5.0           # per-peer read timeout. env: DK_CACHE__PEER_SYNC__TIMEOUT_SECONDS
    cycle_deadline_seconds: null   # hard cap on one cycle; null = interval*0.9. env: DK_CACHE__PEER_SYNC__CYCLE_DEADLINE_SECONDS
                                   # Must be strictly < interval_seconds (config load fails otherwise)

Webapp (webapp:)

πŸ“š Deep details Β· Webapp page

Optional synchronous-HTTP entry point. Disabled by default. When enabled=true, the handler must declare HttpRequestT / HttpResponseT as the third and fourth generic parameters of BaseDrakkarHandler.

webapp:
  enabled: false                   # master switch; false = no FastAPI server. env: DK_WEBAPP__ENABLED
  host: '0.0.0.0'                  # uvicorn bind interface. env: DK_WEBAPP__HOST
  port: 8090                       # uvicorn bind port. env: DK_WEBAPP__PORT
  path: '/process'                 # single POST route; must start with '/'. env: DK_WEBAPP__PATH
  sinks_enabled: false             # when true, route on_message_complete payloads through SinkManager. env: DK_WEBAPP__SINKS_ENABLED
  request_timeout_seconds: 30.0    # per-request budget; > 0. env: DK_WEBAPP__REQUEST_TIMEOUT_SECONDS
  max_concurrent: 64               # per-worker in-flight cap; > 0. 65th concurrent request 503s. env: DK_WEBAPP__MAX_CONCURRENT
  max_body_bytes: 10485760         # request-body cap in bytes; > 0 (413 beyond). env: DK_WEBAPP__MAX_BODY_BYTES

  # Configured tenants. At least one entry is required; default is one
  # anonymous client with rpm=4 so the webapp works out of the box.
  clients:
    - name: anonymous              # tenant name; non-empty string
      token: ''                    # bearer token; '' = anonymous slot (at most one client)
      rpm: 4                       # per-client requests/minute cap; > 0
    - name: tenant-a
      token: 'secret-tenant-a-token'
      rpm: 60

App config

The reserved top-level app: section carries user-defined application config β€” the framework passes it through unvalidated to the handler-declared Pydantic model and exposes the validated instance as self.app_config. Env overrides use the handler’s own prefix (e.g. MYAPP_SCORING__URL), never DK_APP__* (rejected at startup). The Debug UI’s config reference renders the model as its own Application group, secrets masked. See App Config for the full feature.


Environment-variable override cheatsheet

The pattern: DK_<SECTION>__<FIELD> – prefix DK_, double underscore between nesting levels, single underscore within a field name.

Where Path β†’ env var
Top-level cluster_name β†’ DK_CLUSTER_NAME
One level deep kafka.brokers β†’ DK_KAFKA__BROKERS
Two levels deep cache.peer_sync.interval_seconds β†’ DK_CACHE__PEER_SYNC__INTERVAL_SECONDS
Map key (sink instance) sinks.postgres.main-db.dsn β†’ DK_SINKS__POSTGRES__MAIN_DB__DSN
List value ui.expose_env_vars β†’ DK_UI__EXPOSE_ENV_VARS='["GIT_SHA","DEPLOY_ENV"]' (JSON)
Dict value executor.env β†’ DK_EXECUTOR__ENV='{"FOO":"bar"}' (JSON)

Special cases:

  • DK_CONFIG – selects which YAML file to load; not a config field. Excluded from the envβ†’config merge.
  • Hyphens in map keysmain-db becomes MAIN_DB (single underscore) in the env var. Drakkar lowercases the env path on parse, then matches it against the YAML keys.
  • Booleanstrue / false (also 1 / 0, yes / no).
  • Nulls – omit the env var, or use the literal null if the YAML value should be reset.

See Configuration Loading for the precedence order (defaults β†’ YAML β†’ env) and the deep-merge semantics.