Configuration
The .ok/config.yml schema and environment variables.
OpenKnowledge reads YAML config from three places:
- Project:
./.ok/config.yml(this project, committed to git) - User:
~/.ok/global.yml(every project on your machine) - Project-local:
./.ok/local/config.yml(this project on this machine; gitignored). Holds per-machine-per-project preferences such asautoSync.enabled. Maintained by the editor (auto-sync onboarding modal, sync popover, Settings pane Sync section); you don't normally hand-edit it.
All files are optional; defaults cover everything.
You can inspect and edit config three ways:
- Settings pane in the editor: Cmd/Ctrl-, or App menu → Settings…
- Your IDE:
.ok/config.ymlships with a$schemamagic comment for autocomplete and inline field descriptions - CLI:
ok config validatechecks the merged result
Edits made anywhere reflect everywhere; an open Settings pane refreshes live.
Schema
The Scope column says which file a setting belongs in:
- project →
.ok/config.yml(shared in git) - user →
~/.ok/global.yml(personal, all projects) - project-local →
.ok/local/config.yml(this machine only, gitignored)
A key set in a file more specific than its scope (a user-scope key in .ok/config.yml, for example) is ignored.
| Field | Type | Default | Scope | Description |
|---|---|---|---|---|
content.dir | string | "." | project | Content directory, relative to project root. Defaults to . (the project root) even when .ok/ is scaffolded at the git working-tree root from a sub-folder, so opened folder and content scope align by default. To narrow scope to a sub-folder, pass --content-dir <dir> to ok init, or uncomment content.dir in .ok/config.yml post-init. Excluded paths live in .okignore; see Ignore patterns. |
content.attachmentFolderPath | string | "./" | project | Where pasted and dropped assets are stored. "./" colocates beside the current document; "/" targets the content root; "./subdir" targets a subfolder under the current folder; "folder" targets a fixed folder under the content root. |
contentRules.plugins.markdownlint.enabled | boolean | false | project | Enable the markdownlint content-rules plugin for this project (shared via git). Off by default — turn it on in Settings → This project → Plugins. The rules themselves live in your native .markdownlint.* file, not here. |
appearance.theme | "light" | "dark" | "system" | (unset) | user | Editor theme. |
appearance.sidebar.showHiddenFiles | boolean | false | project-local | Show files whose path segments start with .. The sidebar otherwise lists every file on disk under the content directory; tooling internals (.git/, .ok/, node_modules/) stay hidden regardless. Toggled from the sidebar's right-click menu or the macOS View → Show Hidden Files item. |
autoSync.enabled | boolean | null | null | project-local | Per-machine git auto-sync toggle. null means "unanswered"; the editor's onboarding modal triggers on first remote-detected open. |
autoSync.default | boolean | null | null | project | Committed seed for each machine's first-open auto-sync choice: true = on, false = off, null = ask. Lets a maintainer pre-answer the onboarding prompt for everyone who clones; a per-machine autoSync.enabled overrides it. |
editor.wordWrap | boolean | true | user | Soft-wrap long lines in the source-mode CodeMirror editor. A personal preference, not project-shared. Toggle from the Settings pane. |
appearance.preview.autoOpen | boolean | true | user | Whether the agent should open or refresh the OpenKnowledge preview when it edits a doc through the MCP. Default true lets the agent route the preview by host capability: the host's in-app browser (Cursor preview pane, Codex's built-in browser, Claude Code Desktop) when one exists, the system browser otherwise. Set false to keep the agent's hands off your preview window. This is useful when you're already viewing the doc in OK Desktop, a browser tab on a second display, a non-default browser, or any flow where your extensions / accessibility tooling only work in your own browser. The agent then surfaces the URL on request but does not navigate. The change takes effect on the next preview-related tool call. Toggle from Settings → Preferences → "Open preview when agent edits". |
terminal.enabled | boolean | null | null | project-local | Opt-out for the in-app terminal (a real OS shell at full user privilege). On by default; set false to disable it for this project on this machine. |
agents.autoApproveOkTools | boolean | true | user | Auto-approve OpenKnowledge's own MCP tools (and the ok open command on Claude) for agents launched from the built-in docked terminal, so reading and writing the knowledge base runs without a per-call approval prompt. Destructive tools (delete, move, share_link, install) still prompt; other shell commands and non-OpenKnowledge file edits are untouched. Per-machine preference; toggle from Settings → Terminal. Best-effort per agent — Claude uses an allow/deny list, Codex uses its per-server approve mode (and only when its OpenKnowledge entry is already configured). |
telemetry.localSink.enabled | boolean | true | project | Write local diagnostic spans + logs under .ok/local/ for ok diagnose bundle to collect. Local-only: nothing leaves the machine until you run bundle. Set false for sensitive workspaces. |
telemetry.localSink.spans.maxBytes | number | 52428800 (~50 MB) | project | Max size of the local diagnostic spans file before it rotates. |
telemetry.localSink.logs.maxBytes | number | 26214400 (~25 MB) | project | Max size of the local diagnostic logs file before it rotates. |
telemetry.localSink.attributeDenylist | string[] | 8 credential keys | project | Attribute keys whose values are redacted ([REDACTED]) before any local span/log is written. Extends the built-in denylist (authorization, password, cookie, etc.). |
search.semantic.enabled | boolean | false | project-local | Add embeddings-based semantic ranking to the MCP search tool and a By meaning mode to the cmd-K omnibar. Default off. When on and a key is set, the search query and matching page content are sent to the configured embeddings provider (content egress). See Semantic search below. |
search.semantic.baseUrl | string | "https://api.openai.com/v1" | project-local | Base URL of the OpenAI-compatible embeddings API. Override for Azure / self-hosted / other providers. The API key is not stored here — set it with ok embeddings set-key. |
search.semantic.model | string | "text-embedding-3-small" | project-local | Embeddings model id. Must be served by the provider at baseUrl. Changing it re-embeds the corpus. |
search.semantic.dimensions | number | (native) | project-local | Optional output vector size. Omit for the model's native size (1536 for text-embedding-3-small). Set a smaller value to shrink the on-disk cache, trading a little quality. |
search.semantic.similarityFloor | number | (unset) | project-local | Optional hard cutoff (0–1): drops semantic matches whose cosine similarity is below it. Retrieval is rank-based, so most setups leave it unset; set it only for a provider/model whose cosine scale you know. |
linkPreviews.enabled | boolean | true | project-local | Show a rich preview card (site name, page title, description, favicon) when you hover an external link in the editor. Default on (set to false to opt out). When on, hovering an external link sends that link's URL to the destination site to fetch its preview metadata — outbound egress, one request per previewed link. Previews of links to other documents in the project are read from the local index with no network request and are always on. See Link previews below. |
If .ok/config.yml contains a key that has been removed from the schema, config load fails with an error naming the replacement — a stale key is never a silent no-op. ok config migrate (--dry-run to preview; --scope project|user|both) strips removed keys from your config files.
Folder properties
A folder's own metadata lives in that folder's .ok/ directory, not in config.yml: its own open-shape frontmatter (any keys; title / description / tags are the conventional ones) in .ok/frontmatter.yml, and its templates in .ok/templates/. Folder properties describe only their own folder: they are not inherited by the docs inside it. (Templates do resolve leaf-to-root, so a root template is available in every subfolder.) Set them from the editor's Folder properties pane, or via the edit({ folder }) / write({ folder }) MCP tools. See Folders and templates for the full walkthrough.
Ignore patterns
Path exclusions live in a project-root .okignore file using gitignore syntax. .gitignore rules are honored automatically alongside it, and a leading ! re-includes a file .gitignore excluded. Edit patterns from the Ignore patterns section of the Settings pane, the right-click menu in the file tree, or by hand in any editor; the running server picks up changes without a restart. See Ignore patterns for the full UX.
Precedence
CLI flags (when running the bundled ok CLI) > environment variables > project-local config > project config > user config > defaults. Leaf values at higher levels override the same leaf below; arrays replace, they don't concatenate.
Environment variables
Most users never set these — the schema settings above cover the common cases. These have no config.yml key or Settings toggle; they are environment-only.
Common:
| Variable | Sets |
|---|---|
HOST | Server bind host. The --host flag wins over this. |
PORT | Server bind port. The --port flag wins over this. |
OK_MCP_AUTOSTART | Set to 0 to stop MCP tool calls from auto-starting the project's OpenKnowledge server; tools error until you run ok start |
OK_LOG_LEVEL | Log verbosity (info, debug, trace). Falls back to LOG_LEVEL if unset. |
Advanced / operator:
| Variable | Sets |
|---|---|
OPEN_KNOWLEDGE_GITHUB_CLIENT_ID | Override the GitHub OAuth App client ID |
OK_SHOWALL_MAX_ENTRIES | Max entries the sidebar's disk-walk listing returns per level before it stops and marks the list truncated (default 50000) |
OK_BRIDGE_TOLERANCE_TELEMETRY | Set to 1 to record each bridge-tolerance-class fire as a JSONL line in <project>/.ok/local/tolerance-telemetry.jsonl (opt-in, local-only, off by default; unstable diagnostic for triaging markdown-fidelity incidents). Records doc paths in cleartext — independent of telemetry.localSink's attributeDenylist, which redacts span/log attributes, not this file. Deliberately outside the .ok/local/telemetry/ subtree ok diagnose bundle harvests, so the unredacted paths never ship in a bug bundle. |
OTEL_SDK_DISABLED | OpenTelemetry OTLP push gate. Inverted sense: set to false to enable push (it is off by default). |
OK_BUG_REPORT_INTAKE_URL | Base URL of the bug-report intake the desktop app's Help → Report a Bug… upload posts to (e.g. https://openknowledge.ai). Must be https: — plain http: is accepted only for loopback hosts (local testing). Unset (the default), Send makes no network request — it opens a prefilled email draft to support@inkeep.com that you send yourself. |
OK_EMBEDDINGS_API_KEY | Fallback embeddings provider API key for semantic search, used when none is stored on disk. Prefer ok embeddings set-key (writes ~/.ok/secrets.yml) for normal use; the env var is convenient for CI / scripted runs. |
Semantic search
The MCP search tool can fuse an embeddings-based semantic signal into its ranking, so an agent's query surfaces conceptually-related pages even when they share no keywords (a query about "auth retries" can surface a page titled "Session Token Refresh"). It is off by default and additive — with it off, search everywhere (the MCP tool and the cmd-K omnibar) is purely lexical and stays on-machine. With it on, the omnibar gains a By meaning mode alongside its default lexical mode: typing never embeds; pressing Enter fires one semantic search, sending the query to the embeddings provider.
Content egress. When semantic search is enabled and a key is set, the search query and the matching page content are sent to the configured embeddings provider (OpenAI by default). Only content that is already in your corpus is embedded — anything excluded by .okignore / .gitignore is never sent — and embedding is lazy: nothing leaves the machine until a semantic search actually runs. The key lives only in a 0600 ~/.ok/secrets.yml file (never in config.yml, logs, or telemetry).
To turn it on (per machine):
- Store the provider key in
~/.ok/secrets.yml(0600, this machine only):ok embeddings set-key(reads the key from a hidden prompt or stdin). Check it withok embeddings status; remove it withok embeddings clear-key. - Enable it for the project:
ok embeddings enable, or the Settings → This project → Search toggle. Either setssearch.semantic.enabled: truein project-local config (<project>/.ok/local/config.yml), picked up live by a running server;ok embeddings disableturns it off. - (Optional) point at a non-OpenAI provider (Azure / self-hosted):
ok embeddings set-url <url>— or setsearch.semantic.baseUrlin project-local config;ok embeddings clear-urlresets to the default. Tunemodel/dimensionsin config.
The first semantic search kicks off a background embed of the corpus (cents for a whole vault with text-embedding-3-small); vectors are cached incrementally under .ok/local/ and only changed docs re-embed. If the key is missing, the provider errors, or you're offline, search silently degrades to lexical — it never blocks or errors. Each MCP search response reports embedding coverage so an agent knows when vectors are still filling in.
Link previews
Hovering a link in the editor shows a preview card. For internal links (other documents in your project) the card — title, folder, tags, last-edited time, backlink count, and a short excerpt — is built entirely from the local index and file contents: always on, no configuration, and nothing leaves the machine.
For external links, a card shows the destination's site name, page title, description, and favicon. It is on by default; turn it off per machine in Settings.
Desktop app: external link previews are not yet available in the packaged desktop app (its file:// renderer is rejected by the preview route's anti-proxy gate), so this default applies to the browser and ok ui surfaces. Internal document-to-document previews work everywhere.
URL egress. When linkPreviews.enabled is on, hovering an external link sends that link's URL to the destination site — one metadata request per previewed link, from your machine, with no cookies or credentials attached. Responses are size- and time-capped, results are cached locally under .ok/local/, and requests that resolve to private or internal addresses are refused. Nothing else is sent: internal-link cards never touch the network.
To turn it off (per machine): the Settings → This project → Link previews toggle, which sets linkPreviews.enabled: false in project-local config (<project>/.ok/local/config.yml), picked up live by a running server. The setting is enforced by the local server, so with it off no external request is made. A preview that can't be fetched (offline, timeout, the site blocks it) quietly falls back to the plain URL pill.
Verifying changes
The Settings pane validates .ok/config.yml against the schema as you edit and surfaces errors inline. The Ignore patterns section shows a live count of files each pattern matches, so you can confirm scope changes without leaving the editor.