Files
kandev/docs/configuration.md
Jelloeater's Agent 65f27ecbff fix(agents): launch opencode-acp binary-first (native binary on PATH), don't break on stale npm cache (#3836)
Co-authored-by: goose <goose@aaif.dev>
Co-authored-by: Benjamin <benjaminchamwb@gmail.com>
Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
Co-authored-by: João Salavisa <joao.salavisa@gmail.com>
2026-09-21 23:20:43 +01:00

6.7 KiB

Configuration

Kandev's backend reads configuration from three sources, in this order of precedence (later sources override earlier ones):

  1. Built-in defaults (apps/backend/internal/common/config/config.go).
  2. A YAML config file (config.yaml).
  3. Environment variables (KANDEV_*).

Both the file and env vars are optional; the backend boots with sensible defaults out of the box. See docker.md and k8s.md for deployment-specific tables; this page is the full reference.

Config file

The backend looks for config.yaml in, in order:

  • An explicit path passed at startup (used in tests).
  • The current working directory.
  • /etc/kandev/.

A missing file is not an error - defaults plus env vars take over.

Environment variables

All env vars use the KANDEV_ prefix. Nested keys map by replacing . with _ and uppercasing; camelCase becomes one uppercase run (no underscore inserted), because viper does not synthesize a snake_case form.

YAML key Env var
server.port KANDEV_SERVER_PORT
server.webInternalUrl KANDEV_SERVER_WEBINTERNALURL (or alias KANDEV_WEB_INTERNAL_URL)
server.webTitlePrefix KANDEV_SERVER_WEBTITLEPREFIX (or alias KANDEV_WEB_TITLE_PREFIX)
database.dbName KANDEV_DATABASE_DBNAME
logging.maxSizeMb KANDEV_LOGGING_MAXSIZEMB
homeDir KANDEV_HOME_DIR (alias)
logging.level KANDEV_LOG_LEVEL (alias)

The aliases on the right are explicit bindings - see LoadWithPath in config.go for the full list. New keys should follow the deterministic rule (KANDEV_<SECTION>_<KEYUPPERCASE>) unless there is a reason to add an alias.

The make dev launcher uses Dev as the default browser title prefix. The make start-debug launcher keeps production defaults, enables diagnostics, and uses Debug. PR preview environments use Preview. An explicit KANDEV_WEB_TITLE_PREFIX value overrides those environment defaults.

Full config.yaml example

Every key shown here has a default - copying the whole file changes nothing. Use it as a starting point and delete what you don't need to override.

# Kandev root directory. Empty = ~/.kandev (or KANDEV_HOME_DIR if set).
# All workspace artifacts (data, tasks, worktrees, repos, sessions) live here.
homeDir: ""

server:
  host: "0.0.0.0"
  port: 38429              # API + WebSocket + Web UI
  readTimeout: 30          # seconds
  writeTimeout: 30         # seconds
  webInternalUrl: ""       # internal URL the backend uses to call the web app
  webTitlePrefix: ""       # "TEST" -> browser tab title "TEST Kandev"

database:
  driver: "sqlite"         # "sqlite" or "postgres"
  path: ""                 # sqlite: empty = $homeDir/data/kandev.db

  # postgres-only fields below (ignored when driver=sqlite)
  host: "localhost"
  port: 5432
  user: "kandev"           # required when driver=postgres
  password: ""             # required when driver=postgres in most setups
  dbName: "kandev"         # required when driver=postgres
  sslMode: "disable"       # disable | require | verify-ca | verify-full
  maxConns: 25
  minConns: 5

nats:
  url: ""                  # empty = use in-memory event bus
  clusterId: "kandev-cluster"
  clientId: "kandev-client"
  maxReconnects: 10

events:
  namespace: ""            # empty = derive from runtime data identity

docker:
  enabled: true            # disables Docker-based executors when false
  host: ""                 # empty = platform default (unix:///var/run/docker.sock, etc.)
  apiVersion: ""           # empty = auto-negotiate
  tlsVerify: false
  defaultNetwork: "kandev-network"
  volumeBasePath: ""       # empty = /var/lib/kandev/volumes (Linux/macOS)

agent:
  standaloneHost: "127.0.0.1"
  standalonePort: 39429    # agentctl control port

auth:
  jwtSecret: ""            # empty = auto-generate a random dev secret on boot
  tokenDuration: 3600      # seconds

logging:
  level: "info"            # debug | info | warn | error
  format: "text"           # text | json (auto = json when KUBERNETES_SERVICE_HOST is set)
  outputPath: "stdout"     # stdout | stderr | /path/to/file.log

  # Rotation - only applied when outputPath is a file path.
  # Active log files are created with mode 0600 (owner-only).
  maxSizeMb: 100           # rotate at this size; 0 = lumberjack default (100MB)
  maxBackups: 5            # 0 = unlimited
  maxAgeDays: 30           # 0 = unlimited
  compress: true           # gzip rotated files

repositoryDiscovery:
  roots: []                # automatic scan roots; explicit repository paths need not be included
  maxDepth: 5

worktree:
  enabled: true
  defaultBranch: "main"
  cleanupOnRemove: true
  fetchTimeoutSeconds: 60
  pullTimeoutSeconds: 60

repoClone:
  basePath: ""             # empty = $homeDir/repos

debug:
  pprofEnabled: false      # enables /debug/pprof and /api/v1/debug/memory

repositoryDiscovery.roots and repositoryDiscovery.maxDepth bound automatic filesystem scans. They do not authorize explicitly selected repository paths. When a user enters an absolute path in Add Local Repository, Kandev validates that exact accessible Git repository before saving it.

Required vs optional

Almost every field has a default and is optional. The exceptions:

Field When required What happens otherwise
database.user database.driver=postgres Startup fails with database.user is required for postgres driver
database.dbName database.driver=postgres Startup fails with database.dbName is required for postgres driver
database.password database.driver=postgres in most setups (some Postgres configs allow passwordless local auth) Connection fails at runtime
auth.jwtSecret Never strictly required, but in production set an explicit value A random secret is generated on boot - tokens become invalid on restart

Validated value sets (any other value is a startup error):

Field Allowed values
server.port 1-65535
database.driver sqlite, postgres
database.port 1-65535 (only validated when driver=postgres)
database.sslMode disable, require, verify-ca, verify-full (only validated when driver=postgres)
logging.level debug, info, warn, error
logging.format json, text

Tips

  • Env vars override the file. Useful for secrets (KANDEV_DATABASE_PASSWORD) and per-environment knobs (KANDEV_LOG_LEVEL).
  • K8s / Docker: prefer env vars for everything; skip the YAML file entirely.
  • Local dev: drop a config.yaml next to where you run the backend; viper picks it up from the current working directory.
  • Format auto-detection (logging.format): set KANDEV_ENV=production or run inside K8s and you get JSON logs without changing the config.