Configuration
Configuration is layered, lowest to highest precedence:
- Defaults —
internal/config/config.goDefaults(), the single source of truth. knomit.toml— searched next to the binary, then atKNOMIT_HOME/knomit.toml.- 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 and the data layout
Section titled “KNOMIT_HOME and the data layout”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.
Inside a repo, versus inside KNOMIT_HOME
Section titled “Inside a repo, versus inside KNOMIT_HOME”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 areaontology_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.
There is no default repo
Section titled “There is no default repo”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 discovery lockfile
Section titled “Server discovery lockfile”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)
knomit.toml
Section titled “knomit.toml”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-ophost = "localhost"port = "19278"socket = "" # Unix socket path; an *additional* listener alongside host:portontology_root = "kb" # path within git where facts liveonnx_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 syncmethodology_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 = falsebatch = 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:portnetwork_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 tokenuser = ""password = ""ssh_key = "" # path to an SSH private keyauth_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 TTLpipeline_idle_ttl = "60m" # review/hypothesize work-steal session TTLsweep_interval = "5m" # reaper cadence (never disabled)
[discovery]effort_default = "normal" # "normal" | "medium" | "high"confidence_threshold = 0.5 # min confidence to write a discovered proposalblast_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 setmax_members = 5 # max members in a scored bridge seed setquality_floor = 0.0 # min weighted quality score Q (0 disables the gate)w_coh = 1.0 # cohesion weight in Qw_gap = 1.0 # derivation-gap weight in Qw_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|panicfile = "" # non-empty adds a rotating JSON file sink (lumberjack)max_size_mb = 10 # rotate the file sink at this sizemax_backups = 3 # rotated files to keepmax_age_days = 7 # max age for rotated filesslow_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 host-key verification
Section titled “SSH host-key verification”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.
Environment variables
Section titled “Environment variables”Config-backed (override knomit.toml)
Section titled “Config-backed (override knomit.toml)”| Variable | Overrides | Default |
|---|---|---|
KNOMIT_HOME (or KNOMIT_REPO) | data root | ~/.knomit |
KNOMIT_HOST | host | localhost |
KNOMIT_PORT | port | 19278 |
KNOMIT_SOCKET | socket | — |
KNOMIT_READ_ONLY | read_only | false |
KNOMIT_LOCAL_ORIGIN_ROOT | local_origin_root | — (disabled) |
ONNXRUNTIME_SHARED_LIBRARY | onnx_lib_path — use ORT_LIB_PATH, see below | — |
KNOMIT_EMBED_MODEL | embeddings.model | embeddinggemma |
KNOMIT_LLM_MODEL | llm.model | gemini-2.5-flash |
KNOMIT_LLM_PROVIDER | llm.provider | gemini |
KNOMIT_API_KEY | llm.api_key — use the provider’s own variable, see below | — |
KNOMIT_LLM_CACHE | llm.cache | false |
KNOMIT_LLM_BATCH | llm.batch | false |
KNOMIT_GIT_SERVE | git.serve | true |
KNOMIT_GIT_PORT | git.port — /git rides the main port, see below | — |
KNOMIT_GIT_NETWORK_TIMEOUT | git.network_timeout | 120s |
KNOMIT_REMOTE_TOKEN | remote.token | — |
KNOMIT_REMOTE_USER | remote.user | — |
KNOMIT_REMOTE_PASSWORD | remote.password | — |
KNOMIT_REMOTE_SSH_KEY | remote.ssh_key | ~/.knomit/id_ed25519 |
KNOMIT_REMOTE_AUTH | remote.auth_method | auto-detected |
KNOMIT_REMOTE_KNOWN_HOSTS | remote.known_hosts | ~/.knomit/known_hosts |
KNOMIT_METHODOLOGY_MIN_SCORE | methodology_min_score | 0.15 |
KNOMIT_CLUSTER_CACHE_RESOLUTION | cluster_cache.resolution | 4.0 |
KNOMIT_CLUSTER_CACHE_MIN_COMMUNITY_SIZE | cluster_cache.min_community_size | 2 |
KNOMIT_SESSION_TOOL_IDLE_TTL | session.tool_idle_ttl | 15m |
KNOMIT_SESSION_PIPELINE_IDLE_TTL | session.pipeline_idle_ttl | 60m |
KNOMIT_SESSION_SWEEP_INTERVAL | session.sweep_interval | 5m |
KNOMIT_DISCOVERY_EFFORT_DEFAULT | discovery.effort_default | normal |
KNOMIT_DISCOVERY_CONFIDENCE_THRESHOLD | discovery.confidence_threshold | 0.5 |
KNOMIT_DISCOVERY_BLAST_RADIUS_THRESHOLD | discovery.blast_radius_threshold | 1 |
KNOMIT_DISCOVERY_BRIDGE | discovery.bridge | both |
KNOMIT_LOG_FORMAT | log.format | console |
KNOMIT_LOG_LEVEL | log.level | info |
KNOMIT_LOG_FILE | log.file | — (stderr/stdout only) |
KNOMIT_LOG_MAX_SIZE | log.max_size_mb | 10 |
KNOMIT_LOG_MAX_BACKUPS | log.max_backups | 3 |
KNOMIT_LOG_MAX_AGE | log.max_age_days | 7 |
KNOMIT_LOG_SLOW_MS | log.slow_request_ms | 1000 |
KNOMIT_CRASH_LOG | log.crash_file | — (off) |
KNOMIT_RUNTIME_ADDR | runtime.addr | — (off) |
Provider credentials (read directly)
Section titled “Provider credentials (read directly)”These are read by the LLM adapters, not through knomit.toml:
| Variable | Purpose |
|---|---|
ANTHROPIC_API_KEY | Anthropic API key |
GEMINI_API_KEY | Gemini API key (checked first) |
GOOGLE_AI_API_KEY | Gemini API key (fallback) |
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY | AWS credentials for Bedrock |
AWS_REGION | AWS region for Bedrock — no default; an unset region fails at call time |
OLLAMA_HOST | Ollama 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 |
Standalone knobs (no TOML key)
Section titled “Standalone knobs (no TOML key)”| Variable | Read by | Purpose | Default |
|---|---|---|---|
KNOMIT_REFLECT_PROPOSE_CAP | synthesize/reflect | Max new methodologies a reflect step may propose (0 = none) | 1 |
KNOMIT_REFLECT_NOVELTY_THRESHOLD | synthesize/reflect | Cosine floor to reject near-duplicate methodologies | the active model’s calibrated Thresholds.ReflectNovelty — 0.69 for EmbeddingGemma |
KNOMIT_BASE_URL | knomit-bridge claude | Server 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 default | http://localhost:19278 |
KNOMIT_MCP_DEBUG | knomit-bridge | Non-empty → debug logging | — (info) |
KNOMIT_LLM_TRACE | app wiring | Non-empty → wraps the LLM adapter in a tracing adapter that writes every request/response to this path | — (off) |
ORT_LIB_PATH | embeddings | ONNX Runtime shared library — the variable the embedder actually reads | — (probes lib/ next to the binary) |
LLM configuration
Section titled “LLM configuration”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:
| Provider | Variables |
|---|---|
| Gemini (default) | KNOMIT_LLM_PROVIDER=gemini · KNOMIT_LLM_MODEL=gemini-2.5-flash · GEMINI_API_KEY=… |
| Anthropic | KNOMIT_LLM_PROVIDER=anthropic · KNOMIT_LLM_MODEL=claude-sonnet-4-6 · ANTHROPIC_API_KEY=… |
| Bedrock | KNOMIT_LLM_PROVIDER=bedrock · KNOMIT_LLM_MODEL=us.anthropic.claude-sonnet-4-6-v1 · AWS_ACCESS_KEY_ID=… · AWS_SECRET_ACCESS_KEY=… |
| Claude CLI | KNOMIT_LLM_PROVIDER=claudecli — uses the claude CLI (no API key; works with Anthropic Max) |
| Gemini CLI | KNOMIT_LLM_PROVIDER=geminicli — uses the gemini CLI (no API key; works with Google AI Pro) |
| Ollama | KNOMIT_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 / gemini → claudecli / geminicli.
Embeddings are a separate, local model and never call an LLM — see Embeddings.
Verifying integrity
Section titled “Verifying integrity”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:
knomit verify --repo work # verify one repo — there is no default to fall back toknomit verify --all # every ACTIVE repo (archived ones are named as skipped)knomit verify --all --deep # …including per-fact format checksknomit verify --all --json # machine-readable, never truncatedSee the CLI reference for every flag and exit code.