Skip to content

Configuration

Configuration is layered, lowest to highest precedence:

  1. Defaultsinternal/config/config.go Defaults(), the single source of truth.
  2. knomit.toml — searched next to the binary, then at KNOMIT_HOME/knomit.toml.
  3. Environment variables — overlay any value that is set (non-empty).

KNOMIT_HOME is resolved first (it is the config search root) and therefore cannot be overridden from inside the TOML file.

A .env file in the process’s working directory is loaded into the environment before config resolution, so its entries participate as ordinary environment variables at layer 3. It is optional; a missing or unreadable .env is ignored. Variables already present in the environment win — .env does not overwrite them.

KNOMIT_HOME (default ~/.knomit) is the single root for all runtime data. KNOMIT_REPO is a backward-compatible alias. Everything else derives from it:

$KNOMIT_HOME/
├── knomit.toml # optional config file
├── repos/
│ ├── <uid>.db # one repo (SQLite cache; git is source of truth)
│ ├── <uid>.db-shm / -wal # SQLite sidecars
│ ├── <uid>.sessions.db # ephemeral: tool cursors + pipeline work-steal sessions
│ └── archive/
│ ├── <ksuid>.db # archived (deleted-but-recoverable) repo
│ └── <ksuid>.json # archive manifest {id, name, origin, archivedAt}
├── models/
│ └── embeddinggemma/ # cached ONNX model + tokenizer
├── bin/
│ └── knomit-bridge # symlink the desktop app creates on launch
├── crashes/ # crash-report bundles written by the post-mortem reporter
├── dumps/ # goroutine dumps written on SIGUSR1 (server keeps running)
├── control.db # machine-local control plane: the repo registry, origins, lenses
├── running.marker # clean-shutdown marker; present at boot ⇒ previous run crashed
├── known_hosts # SSH host-key pins for git remotes (default location)
├── id_ed25519 / .pub # agent SSH key (signs commits; derives credential-encryption key)
└── …

crashes/, dumps/, and running.marker are the native post-mortem surface — see Observability.

A repo’s name and its filename are no longer the same thing. Each database is repos/<uid>.db, where the uid is an opaque, immutable KSUID; control.db maps that uid to the display name. Renaming a repo therefore touches only a registry row, and two repos can swap names without a file moving. Startup opens what the registry lists — the *.sessions.db sidecars are filtered out, and there is no runtime rescan endpoint.

The layout above is machine-local: caches, keys, models, the registry. None of it syncs. What travels with a knowledge base is the git repo’s own contents, which is a separate layout:

<repo>/
├── kb/ # the ontology root — facts live here
├── README.md # the repo description, read and written by knomit
├── LICENSE # the KB's terms, read and written by knomit
└── .knomit/
├── ontology.yaml # the ontology definition
└── <area>/ # agent-written private state, one dir per area

ontology_root (default kb) names the first of these. The ontology definition itself sits in a private directory — anything with a dot-prefixed path segment is excluded from fact discovery — which is why it is configuration rather than a fact, and why knomit reads it by name. See what a knowledge base repo contains.

Ownership inside .knomit/ splits by depth. Loose files at its root are knomit’s own config and no agent may write them; a subdirectory is an area that agents write private state into. That is why the fact tools refuse .knomit/ontology.yaml — and why the only way to change a live KB’s ontology is out of band, through git.

A fresh install has zero repos, and that is a healthy steady state — startup opens what is already on disk and creates nothing. The web UI’s “no repos” screen is the ordinary first-run view, not an error.

Nothing is privileged: there is no core repo, no config.DefaultRepoName, and archiving your last remaining repo is allowed (it stays restorable). A repo of your own may still be called core; the name simply carries no special meaning.

The consequence for anything that resolves a repo is that it cannot fall back to a hardcoded name and must handle the empty set — which is why knomit verify requires --repo unless you pass --all, and knomit-bridge requires exactly one of --repo or --lens. Repos are born only through POST /api/v1/repos (the web UI’s Manage mode); MCP cannot create them at all.

server.json is written by the desktop app/tray on startup and read by knomit-bridge to find the running server’s port. Contents are {pid, port, version}, mode 0600, written atomically (write-then-rename). Its location is not under KNOMIT_HOME:

  • macOS: ~/Library/Application Support/knomit/server.json
  • Linux: $XDG_STATE_HOME/knomit/server.json (default ~/.local/state/knomit/server.json)

Place a knomit.toml next to the binary or at KNOMIT_HOME/knomit.toml. Top-level keys are bare (not under a table); the rest are [tables]. Defaults shown:

# --- top-level ---
repo = "~/.knomit" # KNOMIT_HOME (the TOML key is "repo"); resolved before TOML, so setting it here is a no-op
host = "localhost"
port = "19278"
socket = "" # Unix socket path; an *additional* listener alongside host:port
ontology_root = "kb" # path within git where facts live
onnx_lib_path = "" # set ORT_LIB_PATH instead (see the note below)
local_origin_root = "" # root for permitted local-path git origins ("" = disabled)
read_only = false # read-only demo: reject mutations, hide git + write tools, pull-only sync
methodology_min_score = 0.15 # composite-score floor for methodology candidates
[llm]
model = "gemini-2.5-flash"
provider = "gemini"
api_key = "" # set the provider's own env var instead (see below)
cache = false
batch = false
[embeddings]
model = "embeddinggemma"
[git]
serve = true # expose the built-in git smart-HTTP server (mounted at /git on the main port)
port = "" # no separate listener: /git rides the main host:port
network_timeout = "120s" # bounds every remote git network op (clone/fetch/push)
max_probe_bytes = 67108864 # 64 MiB: caps the shallow clone the pre-create branch check pulls
[remote]
token = "" # GitHub/PAT token
user = ""
password = ""
ssh_key = "" # path to an SSH private key
auth_method = "" # "token" | "basic" | "ssh" | "none" (inferred if empty)
known_hosts = "" # SSH host-key pins; "" = <KNOMIT_HOME>/known_hosts
[cluster_cache]
resolution = 4.0 # Louvain γ (higher = more, smaller communities)
min_community_size = 2 # communities smaller than this are relabelled as noise
[session]
tool_idle_ttl = "15m" # query/explain cursor TTL
pipeline_idle_ttl = "60m" # review/hypothesize work-steal session TTL
sweep_interval = "5m" # reaper cadence (never disabled)
[discovery]
effort_default = "normal" # "normal" | "medium" | "high"
confidence_threshold = 0.5 # min confidence to write a discovered proposal
blast_radius_threshold = 1 # min reach for a backward keystone proposal (0 = off)
bridge = "both" # "domain" | "entity" | "both"
# Bridge quality gate — TOML only, no KNOMIT_* env override (see below)
coh_floor = 0.5 # min intra-cluster cohesion for a bridge seed set
max_members = 5 # max members in a scored bridge seed set
quality_floor = 0.0 # min weighted quality score Q (0 disables the gate)
w_coh = 1.0 # cohesion weight in Q
w_gap = 1.0 # derivation-gap weight in Q
w_spec = 1.0 # specificity weight in Q
[log]
format = "console" # "console" (human, stderr) | "json" (structured, stdout)
level = "info" # zerolog level: trace|debug|info|warn|error|fatal|panic
file = "" # non-empty adds a rotating JSON file sink (lumberjack)
max_size_mb = 10 # rotate the file sink at this size
max_backups = 3 # rotated files to keep
max_age_days = 7 # max age for rotated files
slow_request_ms = 1000 # log HTTP/MCP requests slower than this at WARN (0 = off)
crash_file = "" # non-empty redirects fd 2 (stderr) to persist fatal/CGO tracebacks
[runtime]
addr = "" # diagnostics port (pprof + /metrics + /runtime/*); "" = off. Bind local only.

SSH remotes verify the server’s host key against an OpenSSH known_hosts file. remote.known_hosts (env KNOMIT_REMOTE_KNOWN_HOSTS) selects that file; the value is tilde-expanded, and an unset value resolves to <KNOMIT_HOME>/known_hosts. Point it at ~/.ssh/known_hosts to share the pins with your interactive ssh client.

Verification is trust-on-first-use, because syncs run unattended and cannot prompt:

  • An unknown host is accepted and its key is appended to the file.
  • A host already listed under a different key is rejected.

Per-request auth specs — a credential supplied with a single API call — carry only the credential and inherit the operator’s known_hosts setting, so every SSH path pins against the same file.

VariableOverridesDefault
KNOMIT_HOME (or KNOMIT_REPO)data root~/.knomit
KNOMIT_HOSThostlocalhost
KNOMIT_PORTport19278
KNOMIT_SOCKETsocket
KNOMIT_READ_ONLYread_onlyfalse
KNOMIT_LOCAL_ORIGIN_ROOTlocal_origin_root— (disabled)
ONNXRUNTIME_SHARED_LIBRARYonnx_lib_path — use ORT_LIB_PATH, see below
KNOMIT_EMBED_MODELembeddings.modelembeddinggemma
KNOMIT_LLM_MODELllm.modelgemini-2.5-flash
KNOMIT_LLM_PROVIDERllm.providergemini
KNOMIT_API_KEYllm.api_key — use the provider’s own variable, see below
KNOMIT_LLM_CACHEllm.cachefalse
KNOMIT_LLM_BATCHllm.batchfalse
KNOMIT_GIT_SERVEgit.servetrue
KNOMIT_GIT_PORTgit.port/git rides the main port, see below
KNOMIT_GIT_NETWORK_TIMEOUTgit.network_timeout120s
KNOMIT_REMOTE_TOKENremote.token
KNOMIT_REMOTE_USERremote.user
KNOMIT_REMOTE_PASSWORDremote.password
KNOMIT_REMOTE_SSH_KEYremote.ssh_key~/.knomit/id_ed25519
KNOMIT_REMOTE_AUTHremote.auth_methodauto-detected
KNOMIT_REMOTE_KNOWN_HOSTSremote.known_hosts~/.knomit/known_hosts
KNOMIT_METHODOLOGY_MIN_SCOREmethodology_min_score0.15
KNOMIT_CLUSTER_CACHE_RESOLUTIONcluster_cache.resolution4.0
KNOMIT_CLUSTER_CACHE_MIN_COMMUNITY_SIZEcluster_cache.min_community_size2
KNOMIT_SESSION_TOOL_IDLE_TTLsession.tool_idle_ttl15m
KNOMIT_SESSION_PIPELINE_IDLE_TTLsession.pipeline_idle_ttl60m
KNOMIT_SESSION_SWEEP_INTERVALsession.sweep_interval5m
KNOMIT_DISCOVERY_EFFORT_DEFAULTdiscovery.effort_defaultnormal
KNOMIT_DISCOVERY_CONFIDENCE_THRESHOLDdiscovery.confidence_threshold0.5
KNOMIT_DISCOVERY_BLAST_RADIUS_THRESHOLDdiscovery.blast_radius_threshold1
KNOMIT_DISCOVERY_BRIDGEdiscovery.bridgeboth
KNOMIT_LOG_FORMATlog.formatconsole
KNOMIT_LOG_LEVELlog.levelinfo
KNOMIT_LOG_FILElog.file— (stderr/stdout only)
KNOMIT_LOG_MAX_SIZElog.max_size_mb10
KNOMIT_LOG_MAX_BACKUPSlog.max_backups3
KNOMIT_LOG_MAX_AGElog.max_age_days7
KNOMIT_LOG_SLOW_MSlog.slow_request_ms1000
KNOMIT_CRASH_LOGlog.crash_file— (off)
KNOMIT_RUNTIME_ADDRruntime.addr— (off)

These are read by the LLM adapters, not through knomit.toml:

VariablePurpose
ANTHROPIC_API_KEYAnthropic API key
GEMINI_API_KEYGemini API key (checked first)
GOOGLE_AI_API_KEYGemini API key (fallback)
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEYAWS credentials for Bedrock
AWS_REGIONAWS region for Bedrock — no default; an unset region fails at call time
OLLAMA_HOSTOllama server base URL; unset defaults to http://localhost:11434. Health-checked at adapter creation (GET /api/tags, 5s) — an unreachable server is a hard error
VariableRead byPurposeDefault
KNOMIT_REFLECT_PROPOSE_CAPsynthesize/reflectMax new methodologies a reflect step may propose (0 = none)1
KNOMIT_REFLECT_NOVELTY_THRESHOLDsynthesize/reflectCosine floor to reject near-duplicate methodologiesthe active model’s calibrated Thresholds.ReflectNovelty0.69 for EmbeddingGemma
KNOMIT_BASE_URLknomit-bridge claudeServer URL for the claude init / claude hook helpers. Does not affect the MCP proxy — that resolves its base URL from the positional argument, then server.json, then the hardcoded defaulthttp://localhost:19278
KNOMIT_MCP_DEBUGknomit-bridgeNon-empty → debug logging— (info)
KNOMIT_LLM_TRACEapp wiringNon-empty → wraps the LLM adapter in a tracing adapter that writes every request/response to this path— (off)
ORT_LIB_PATHembeddingsONNX Runtime shared library — the variable the embedder actually reads— (probes lib/ next to the binary)

Synthesis is the only LLM-backed feature. Set both the provider and the model — the shipped default is provider = "gemini", and an explicit provider is always used as-is, so changing only the model leaves you on the Gemini adapter:

ProviderVariables
Gemini (default)KNOMIT_LLM_PROVIDER=gemini · KNOMIT_LLM_MODEL=gemini-2.5-flash · GEMINI_API_KEY=…
AnthropicKNOMIT_LLM_PROVIDER=anthropic · KNOMIT_LLM_MODEL=claude-sonnet-4-6 · ANTHROPIC_API_KEY=…
BedrockKNOMIT_LLM_PROVIDER=bedrock · KNOMIT_LLM_MODEL=us.anthropic.claude-sonnet-4-6-v1 · AWS_ACCESS_KEY_ID=… · AWS_SECRET_ACCESS_KEY=…
Claude CLIKNOMIT_LLM_PROVIDER=claudecli — uses the claude CLI (no API key; works with Anthropic Max)
Gemini CLIKNOMIT_LLM_PROVIDER=geminicli — uses the gemini CLI (no API key; works with Google AI Pro)
OllamaKNOMIT_LLM_PROVIDER=ollama · KNOMIT_LLM_MODEL=<any model the server has pulled> · OLLAMA_HOST=… (default http://localhost:11434). No API key. Never inferred from a model name, so the provider must be set explicitly

Provider names are matched exactly: gemini, anthropic, bedrock, claudecli, geminicli, ollama. Anything else fails with unknown provider "…". Note there is no hyphen in the CLI names.

Inference from the model name happens only when the provider is explicitly empty (KNOMIT_LLM_PROVIDER= or provider = "" in knomit.toml). In that case claude-*anthropic, gemini-*gemini, anthropic.* / us.* / eu.*bedrock, and the bare names claude / geminiclaudecli / geminicli.

Embeddings are a separate, local model and never call an LLM — see Embeddings.

The knomit verify subcommand runs integrity checks against a live on-disk repo. It is read-only, but it holds the read lock on every branch it checks for its whole run, so it blocks every writer on those branches — stop any writing agent rather than firing it at a busy one:

Terminal window
knomit verify --repo work # verify one repo — there is no default to fall back to
knomit verify --all # every ACTIVE repo (archived ones are named as skipped)
knomit verify --all --deep # …including per-fact format checks
knomit verify --all --json # machine-readable, never truncated

See the CLI reference for every flag and exit code.