---
title: Configuration
icon: LuSettings
description: 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 as `autoSync.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.yml` ships with a `$schema` magic comment for autocomplete and inline field descriptions
- **CLI**: `ok config validate` checks the merged result and reports wrong-scope keys

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). Setting a key in the wrong-scope file is ignored; `ok config validate` reports it.

| 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, uncomment `content.dir` in `.ok/config.yml` post-init. Excluded paths live in `.okignore`; see [Ignore patterns](/docs/features/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. |
| `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. |
| `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"**. |
| `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. 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](#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. |

## 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](/docs/advanced/folders-and-templates#folder-properties) 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](/docs/features/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 disable auto-starting the MCP server |
| `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_EMBEDDINGS_API_KEY` | Fallback embeddings provider API key for [semantic search](#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 is byte-for-byte today's lexical ranking, and the cmd-K omnibar stays purely lexical and instant either way (semantic applies only to the agent-facing MCP tool).

<Callout type="warn">
  **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 an agent actually runs a semantic search. The key lives only in a 0600 `~/.ok/secrets.yml` file (never in `config.yml`, logs, or telemetry).
</Callout>

To turn it on (per machine):

1. 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 with `ok embeddings status`.
2. Set `search.semantic.enabled: true` in project-local config (`<project>/.ok/local/config.yml`).
3. (Optional) point at a non-OpenAI provider via `search.semantic.baseUrl` / `model` / `dimensions`.

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.

## 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.
