Configuration reference

SysKnife reads configuration from three places, in lowest-to-highest priority:

  1. Built-in defaults — compiled into sysknife-brain and sysknife-core
  2. ~/.config/sysknife/config.toml — optional, user-owned
  3. Environment variables — always win

Set whatever's stable for your install in config.toml; override with env vars when you need to (CI runs, ad-hoc experiments, distro packagers).

config.toml reference

Path: $XDG_CONFIG_HOME/sysknife/config.toml, falling back to ~/.config/sysknife/config.toml. The daemon and CLI read this on every startup; the GUI reloads it after the wizard finishes.

# ─── [daemon] ────────────────────────────────────────────────────────
[daemon]
# Unix socket path the daemon listens on. CLI / shell connect here.
socket   = "/run/sysknife/daemon.sock"
# SQLite database path for the local audit log.
# Production deployments should switch to [storage] backend = "postgres"
# (see below).
database = "/var/lib/sysknife/daemon.sqlite"

# ─── [llm] ───────────────────────────────────────────────────────────
[llm]
provider     = "ollama"          # ollama | anthropic | openai | gemini |
                                 # groq | deepseek | mistral | xai
model        = "qwen3:8b"        # provider-specific model identifier
ollama_url   = "http://localhost:11434"
anthropic_url = "https://api.anthropic.com"
max_turns    = 10                # planning loop turn limit (>= 1)

# Optional: override the auto-detected thinking mode for Ollama.
# Default: auto-detect from the model name (qwen3 / qwq / deepseek-r → true).
# Set to `false` on CPU-only hosts running thinking models — thinking
# traces exceed Ollama's internal request timeout on 4 vCPUs.
# ollama_think = false

# ─── [storage] ───────────────────────────────────────────────────────
# Audit-log backend. Default (absent or backend = "sqlite") uses the
# local rusqlite store at [daemon].database. Production deployments
# should set backend = "postgres" — the SQLite path dies with the host.
[storage]
backend = "postgres"
url     = "postgres://sysknife:${PG_PASSWORD}@db.example.com:5432/audit?sslmode=verify-full"

[storage.pool]
max_connections          = 8
acquire_timeout_secs     = 10
statement_cache_capacity = 100   # set to 0 for transaction-mode poolers

# ─── [policy] ────────────────────────────────────────────────────────
# Per-action risk-level overrides. Map from action name → risk level
# ("Low" | "Medium" | "High"). Validated at startup; unknown action
# names or attempted downgrades are fatal — overrides may only RAISE
# the minimum role required, never lower it.
[policy.risk_overrides]
InstallFlatpak = "High"     # require Admin in this org (default: Medium/Dev)

# ─── [audit.forward] ─────────────────────────────────────────────────
# Optional SIEM forwarding. Best-effort — never blocks daemon execution.
# Phase 1 ships RFC 5424 syslog over UDP; CEF and NDJSON-over-TCP
# arrive in follow-up PRs.
[audit.forward.syslog]
host     = "siem.internal:514"
facility = 1                 # 1 = user-level (default)

The transaction database is the durable audit record. Safety-fence JSONL, journald watermarks, and UDP syslog forwarding are operational signals and may be unavailable or lossy. PostgreSQL migrations run automatically at daemon startup; backup and restore requirements are documented in storage-cloud.md.

Environment variables

Env vars always win over config.toml. Useful for CI runs, distro packagers, and ad-hoc experiments. Variable names mirror the config file's section.field path.

VariableDefaultWhat it sets
SYSKNIFE_LISTEN_URIunix://$XDG_RUNTIME_DIR/sysknife/daemon.sock (falls back to unix:///tmp/sysknife-$UID.sock if XDG_RUNTIME_DIR is unset; production systemd unit sets unix:///run/sysknife/daemon.sock)Daemon socket / vsock URI
SYSKNIFE_DATABASE_PATH$XDG_STATE_HOME/sysknife/daemon.sqlite (falls back to ~/.local/state/sysknife/daemon.sqlite; production systemd unit sets /var/lib/sysknife/daemon.sqlite)SQLite audit log path
SYSKNIFE_LLM_PROVIDERauto-detectLLM provider name (8 supported)
SYSKNIFE_LLM_MODELprovider defaultModel identifier
SYSKNIFE_OLLAMA_URLhttp://localhost:11434Ollama base URL
SYSKNIFE_OLLAMA_THINKauto-detecttrue / false thinking-mode override
SYSKNIFE_ANTHROPIC_URLhttps://api.anthropic.comAnthropic base URL
SYSKNIFE_BRAIN_MAX_TURNS10Planning loop turn limit
SYSKNIFE_MAX_RPM20Rate limit (requests / 60s sliding window)
SYSKNIFE_AUDIT_KEY_PATH<db_dir>/audit-keyEd25519 signing key path for the audit chain
SYSKNIFE_CHECKPOINT_DBPostgres URL for audit checkpoint external anchoring (keeps DB credentials off the command line)
SYSKNIFE_SOCKETfalls back to the same default as SYSKNIFE_LISTEN_URICLI / MCP daemon address
SYSKNIFE_TOKENVsock auth token (when daemon runs in a VM)
XDG_CONFIG_HOME~/.configBase path for sysknife/config.toml

Provider API keys

Required when the corresponding provider is selected:

  • OPENAI_API_KEY — OpenAI
  • ANTHROPIC_API_KEY — Anthropic
  • GEMINI_API_KEY — Gemini
  • GROQ_API_KEY — Groq
  • DEEPSEEK_API_KEY — DeepSeek
  • MISTRAL_API_KEY — Mistral
  • XAI_API_KEY — xAI
  • none — Ollama (local, no key)

Daemon-only configuration

These environment variables are read only by sysknife-daemon, not by the CLI / shell:

VariablePurpose
SYSKNIFE_AUDIT_KEY_PATHEd25519 audit signing key path (default: alongside the database)

Validating your config

sysknife doctor

Reports the resolved configuration (socket, host, provider, model, audit backend) plus a quick chain-integrity check. A failing doctor is the fastest way to catch a typo'd env var or a bad path.

Where each setting lives in the source

For maintainers — the canonical types are:

  • crates/sysknife-core/src/config.rsLacsConfig, DaemonSection, LlmSection, PolicySection, AuditSection, StorageSection, StoragePoolSection
  • crates/sysknife-brain/src/config.rsBrainConfig, ProviderConfig
  • crates/sysknife-core/src/lib.rsdefault_listen_uri, default_database_path, prefs_path

Adding a new config knob: add the field to LacsConfig, surface the env var in apply_defaults_to_env, and update this document. A test in crates/sysknife-core/src/config.rs should cover the new field.