# Analytics and health Source: https://docs.kiwifs.com/api/analytics Knowledge health dashboard, per-page health checks, comments, sharing, and real-time events. ## Health dashboard Get an overview of your knowledge base health. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/analytics' ``` ```json theme={null} { "total_pages": 142, "stale_pages": 8, "orphan_pages": 5, "broken_links": 3, "empty_pages": 1, "pages_without_frontmatter": 12, "link_coverage_pct": 96.5, "recently_updated": [ {"path": "concepts/auth.md", "updated": "2026-04-25T14:30:00Z"}, {"path": "concepts/billing.md", "updated": "2026-04-24T09:15:00Z"} ] } ``` Total number of pages. Pages past their `review-by` date in frontmatter. Pages with no incoming backlinks. Wiki links pointing to nonexistent pages. Pages with no content beyond frontmatter. Pages missing YAML frontmatter. Percentage of wiki links that resolve to existing pages. Most recently modified pages. ## Analytics overview Compact dashboard for the web UI engagement panel: ```bash theme={null} curl 'http://localhost:3333/api/kiwi/analytics/overview' ``` ## Page views ```bash theme={null} curl 'http://localhost:3333/api/kiwi/analytics/views' curl 'http://localhost:3333/api/kiwi/analytics/views/v2?path=concepts/auth.md' ``` `views/v2` returns richer per-page engagement when tracking is enabled. ## Search analytics ```bash theme={null} curl 'http://localhost:3333/api/kiwi/analytics/searches' curl 'http://localhost:3333/api/kiwi/analytics/failed-searches' ``` Failed searches help identify content gaps agents could not answer. ## Trends and content gaps ```bash theme={null} curl 'http://localhost:3333/api/kiwi/analytics/trends' curl 'http://localhost:3333/api/kiwi/analytics/content-gaps' ``` Dismiss a content gap suggestion: ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/analytics/content-gaps/dismiss' \ -H 'Content-Type: application/json' \ -d '{"query": "oauth refresh token"}' ``` ## Import sources analytics ```bash theme={null} curl 'http://localhost:3333/api/kiwi/analytics/sources' ``` Summarizes pages by `_source` frontmatter from imports. ## Per-page health check Analyze the health of a single page. File path to check. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/health-check?path=concepts/auth.md' ``` ```json theme={null} { "path": "concepts/auth.md", "word_count": 342, "link_count": 5, "backlink_count": 8, "days_since_update": 2, "issues": [] } ``` When issues are detected, they appear in the `issues` array: ```json theme={null} { "issues": [ {"type": "stale", "message": "Page is 45 days past its review date"}, {"type": "broken_link", "message": "Link [[payments-v1]] does not resolve"} ] } ``` ## Stale pages List pages that are past their review date. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/stale' ``` ```json theme={null} [ { "path": "concepts/legacy-auth.md", "title": "Legacy Authentication", "review_by": "2026-03-01", "days_overdue": 55 } ] ``` ## Contradictions Detect pages with potentially conflicting claims. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/contradictions' ``` ```json theme={null} [ { "pages": ["concepts/auth.md", "runbooks/login-flow.md"], "claim": "session timeout duration", "details": "auth.md says 30 minutes, login-flow.md says 1 hour" } ] ``` ## Janitor scan Run a full knowledge health scan across the entire knowledge base. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/janitor' ``` ```json theme={null} { "stale": 8, "orphans": 5, "broken_links": 3, "empty": 1, "missing_frontmatter": 12, "recommendations": [ "Link orphan page concepts/caching.md from a parent topic", "Update stale page runbooks/deploy-v1.md (90 days overdue)", "Fix broken link [[payments-v1]] in concepts/billing.md" ] } ``` *** ## Memory report Get an overview of your episodic memory structure — useful for agents that use the episode/merge pattern. ```http theme={null} GET /api/kiwi/memory/report ``` Path prefix for episodes directory. Uses the configured default if omitted. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/memory/report' ``` ```json theme={null} { "episodic_count": 23, "merged_from_refs": 15, "episodic_files": ["episodes/ep-001.md", "episodes/ep-002.md"], "unmerged": 8, "warnings": ["Episode ep-012.md has no merge target"] } ``` ## Context Retrieve the knowledge base context files used by agents — the playbook, schema description, and index. ```http theme={null} GET /api/kiwi/context ``` ```bash theme={null} curl 'http://localhost:3333/api/kiwi/context' ``` ```json theme={null} { "playbook": "# Playbook\n\nRules for how agents should interact...", "schema": "# Schema\n\nFrontmatter fields and their meanings...", "index": "# Index\n\nTop-level knowledge structure..." } ``` These map to `.kiwi/playbook.md`, `.kiwi/SCHEMA.md`, and `.kiwi/index.md`. ## Theme Read or update the web UI theme. ### Get theme ```bash theme={null} curl 'http://localhost:3333/api/kiwi/theme' ``` ### Set theme ```bash theme={null} curl -X PUT 'http://localhost:3333/api/kiwi/theme' \ -H "Content-Type: application/json" \ -d '{"primary": "#6cbe45", "mode": "light"}' ``` Returns `403` if `ui.theme_locked = true` in configuration. *** ## Comments Add, list, resolve, and delete inline comments on pages. ### List comments File path to list comments for. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/comments?path=concepts/auth.md' ``` ```json theme={null} [ { "id": "cmt_01", "path": "concepts/auth.md", "line": 15, "author": "agent:reviewer", "body": "Should this mention refresh tokens?", "resolved": false, "created": "2026-04-25T10:00:00Z" } ] ``` ### Add a comment ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/comments?path=concepts/auth.md' \ -H "Content-Type: application/json" \ -H "X-Actor: agent:reviewer" \ -d '{"line": 15, "body": "Should this mention refresh tokens?"}' ``` ### Resolve a comment ```bash theme={null} curl -X PATCH 'http://localhost:3333/api/kiwi/comments/cmt_01' \ -H "Content-Type: application/json" \ -d '{"resolved": true}' ``` ### Delete a comment ```bash theme={null} curl -X DELETE 'http://localhost:3333/api/kiwi/comments/cmt_01' ``` *** ## Sharing Create and manage share links for pages. ### Create a share link ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/share' \ -H "Content-Type: application/json" \ -H "X-Actor: agent:sharer" \ -d '{"path": "concepts/auth.md", "expiresIn": "168h", "password": "optional-secret"}' ``` `expiresIn` is a Go duration string (`168h` for seven days). Omit it for links that never expire. ```json theme={null} { "id": "a1b2c3d4", "path": "concepts/auth.md", "token": "", "expiresAt": "2026-05-09T14:30:00Z", "createdBy": "agent:sharer", "createdAt": "2026-05-02T14:30:00Z", "viewCount": 0 } ``` Readers open `GET /api/kiwi/public/:token` (see [Utilities](/api/utilities#public-routes)). ### List share links ```bash theme={null} curl 'http://localhost:3333/api/kiwi/share?path=concepts/auth.md' ``` `path` is **required** — the handler returns every active link for that page. ### Revoke a share link ```bash theme={null} curl -X DELETE 'http://localhost:3333/api/kiwi/share/a1b2c3d4' ``` Returns JSON `{ "revoked": "" }` using the share **`id`** field (not the long `token`). *** ## Real-time events (SSE) Subscribe to a Server-Sent Events stream that emits events on every write and delete. Use this for real-time UI updates or external integrations. ```bash theme={null} curl -N 'http://localhost:3333/api/kiwi/events' ``` ``` event: write data: {"path":"concepts/auth.md","actor":"agent:docs-writer","timestamp":"2026-04-25T14:30:00Z"} event: delete data: {"path":"concepts/old-page.md","actor":"agent:cleanup","timestamp":"2026-04-25T14:31:00Z"} event: write data: {"path":"concepts/billing.md","actor":"agent:importer","timestamp":"2026-04-25T14:32:00Z"} ``` The SSE connection stays open indefinitely. Use `curl -N` (no-buffer) to see events as they arrive. In production, implement reconnection logic with the `Last-Event-ID` header. ### Event types | Event | Trigger | | -------- | ----------------------------- | | `write` | A file was created or updated | | `delete` | A file was deleted | | `bulk` | A bulk write completed | | `import` | An import completed | # Canvas Source: https://docs.kiwifs.com/api/canvas List, read, write, patch, query, and auto-layout .canvas.json files. Base path: `http://localhost:3333/api/kiwi`. Canvas paths must end with **`.canvas.json`**. ## List canvases ```http theme={null} GET /canvases ``` ```json theme={null} { "canvases": ["maps/site.canvas.json", "research/ideas.canvas.json"] } ``` ## Read canvas ```http theme={null} GET /canvas?path=maps/site.canvas.json ``` Returns the raw JSON document with `Content-Type: application/json`. ## Write canvas ```http theme={null} PUT /canvas?path=maps/site.canvas.json Content-Type: application/json ``` Body must parse as JSON with **`nodes`** and **`edges`** arrays (empty arrays are allowed). Invalid JSON returns **`400`**. ## Patch canvas Apply a batch of atomic operations without replacing the full document. If any operation fails, the whole batch is rejected. ```http theme={null} PATCH /canvas?path=maps/site.canvas.json Content-Type: application/json ``` ```json theme={null} { "operations": [ { "op": "add_node", "node": { "id": "n1", "type": "file", "x": 0, "y": 0, "data": { "path": "concepts/auth.md" } } }, { "op": "update_node", "id": "n1", "node": { "x": 120 } }, { "op": "remove_node", "id": "n2" }, { "op": "add_edge", "edge": { "id": "e1", "fromNode": "n1", "toNode": "n3" } }, { "op": "update_edge", "id": "e1", "edge": { "label": "depends on" } }, { "op": "remove_edge", "id": "e1" } ] } ``` Supported `op` values: `add_node`, `update_node`, `remove_node`, `add_edge`, `update_edge`, `remove_edge`. Nodes inserted. Nodes merged in place. Nodes deleted (connected edges removed too). Edges inserted. Edges merged in place. Edges deleted. ## Delete canvas ```http theme={null} DELETE /canvas?path=maps/site.canvas.json ``` ## Query canvas nodes ```http theme={null} GET /canvas/query?path=maps/site.canvas.json&q=auth&type=file&connected=n1&nodes_only=true ``` Canvas file path. Filter nodes by label or metadata text. Filter by node type. Return nodes connected to the given node ID. Return only nodes. Return only edges. ## Auto-layout Reposition existing nodes in a canvas using a layout algorithm. This operates on the current canvas content only — it does not generate new nodes from the wiki-link graph (use `POST /canvas/generate` for that). ```http theme={null} POST /canvas/auto-layout?path=maps/site.canvas.json Content-Type: application/json ``` ```json theme={null} { "layout": "dot" } ``` Canvas file to update in place. Graphviz engine: `dot`, `neato`, `fdp`, or `circo`. ## Generate from link graph ```http theme={null} POST /canvas/generate Content-Type: application/json ``` ```json theme={null} { "path": "auto/graph.canvas.json", "layout": "hierarchical", "folder": "concepts/", "colorize": true } ``` * `path` — output file; default `canvas.canvas.json`. A `.canvas.json` suffix is added if missing. * `layout` — `hierarchical` (default), `radial`, `force`, or `circular`. * `folder` — optional prefix; only edges whose endpoints live under this folder are included. * `colorize` — boolean, default `true`. Returns **`503`** if the link index is unavailable. `POST /canvas/generate` and `PATCH /canvas` are REST-only. MCP exposes `kiwi_canvas_list`, `kiwi_canvas_read`, and `kiwi_canvas_write` — not generate or patch. ## MCP parity `kiwi_canvas_list`, `kiwi_canvas_read`, `kiwi_canvas_write` — see [MCP](/concepts/mcp). # Claims Source: https://docs.kiwifs.com/api/claims Claim, release, and list cooperative write leases on file paths. Claims require a configured claim store. Otherwise endpoints return **`503`** with `"claims not enabled"`. **`X-Actor` is mandatory** for claim and release. Base path: `http://localhost:3333/api/kiwi`. ## Claim a path ```http theme={null} POST /claim Content-Type: application/json ``` ```json theme={null} { "path": "runbooks/deploy.md", "lease_duration": "45m" } ``` * `lease_duration` is optional (Go duration syntax). The server enforces **1m ≤ lease ≤ 24h** (default **30m**). * Returns **`409`** JSON `{ "error": "already claimed", "active_claim": { ... } }` when another actor holds the lease. ## Release ```http theme={null} DELETE /claim Content-Type: application/json ``` ```json theme={null} { "path": "runbooks/deploy.md" } ``` Returns **`403`** if the caller is not the current holder. ## List active claims ```http theme={null} GET /claims ``` ```json theme={null} { "claims": [/* active claim objects */] } ``` ## MCP parity `kiwi_claim`, `kiwi_release`, `kiwi_claims_list` — see [MCP](/concepts/mcp). # Drafts Source: https://docs.kiwifs.com/api/drafts Create, edit, merge, or discard git-backed draft workspaces under /api/kiwi/drafts. All paths below are under `http://localhost:3333/api/kiwi` unless you use [multi-space URLs](/api/overview) or the [`X-Kiwi-Space`](/api/overview) header. Drafts return **`501 Not Implemented`** when the server was started without a draft manager. ## Create draft ```http theme={null} POST /drafts ``` Optional JSON body: ```json theme={null} { "actor": "agent:planner" } ``` If `actor` is empty, the server uses `X-Actor`, then falls back to `api`. **Response `201`:** `{ "id", "branch", "actor", "created_at" }` ## List drafts ```http theme={null} GET /drafts ``` Returns a JSON array of draft summaries (same fields as create). ## Get draft ```http theme={null} GET /drafts/:id ``` ## Diff draft against main ```http theme={null} GET /drafts/:id/diff ``` Returns `{ "diff": "" }`. ## Merge or discard ```http theme={null} POST /drafts/:id/merge ``` Returns `{ "status": "merged" }`. Conflicts return `409`. ```http theme={null} DELETE /drafts/:id ``` Returns **`204`** on success. ## Files inside a draft | Method | Path | Notes | | -------- | --------------------------- | ------------------------------------------------------------------------------------- | | `GET` | `/drafts/:id/file?path=...` | Raw markdown body; **`ETag`** header set. | | `PUT` | `/drafts/:id/file?path=...` | Body is markdown; uses `X-Actor` (default `draft-api`). Returns `{ "path", "etag" }`. | | `DELETE` | `/drafts/:id/file?path=...` | Deletes file inside the draft branch. **`204`**. | | `GET` | `/drafts/:id/tree?path=` | Same tree shape as [`GET /tree`](/api/files); omit `path` for root. | ## MCP parity Use the `kiwi_draft_*` tools from [MCP](/concepts/mcp) for the same operations from Claude, Cursor, or other MCP clients. # Files Source: https://docs.kiwifs.com/api/files Read, write, delete, and bulk-manage files through the KiwiFS REST API. ## Directory tree Retrieve the directory tree as a JSON array. Root path to list. Omit to list the entire knowledge base. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/tree?path=concepts' ``` Array of tree nodes. Full path relative to root. File or directory name. Whether this node is a directory. File size in bytes (0 for directories). Nested tree nodes (directories only). ```json theme={null} [ { "path": "concepts", "name": "concepts", "isDir": true, "size": 0, "children": [ {"path": "concepts/auth.md", "name": "auth.md", "isDir": false, "size": 1420, "children": null} ] } ] ``` ## Read a file Retrieve raw markdown content. The response includes an `ETag` header containing the git blob SHA for optimistic locking. File path relative to the knowledge root. ```bash theme={null} curl -i 'http://localhost:3333/api/kiwi/file?path=concepts/auth.md' ``` ``` HTTP/1.1 200 OK Content-Type: text/markdown ETag: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2" # Authentication OAuth2 + JWT based authentication system... ``` ## Read a symlink target When a path in the knowledge root is a symbolic link, resolve its target without following into arbitrary filesystem paths outside the guard: ```http theme={null} GET /api/kiwi/readlink?path=link/to/page.md ``` Returns **plain text** body with the symlink target. **`404`** when the path does not exist; **`400`** when the path exists but is not a symlink. ## Write a file Create or update a file. The request body is raw markdown. KiwiFS creates a git commit for each write. File path relative to the knowledge root. Git commit author attribution (e.g. `agent:my-agent`). Lineage tracking (e.g. `run:run-249`). ETag from a previous read. If the file has changed since your read, the server returns `409 Conflict`. ```bash Create a new file theme={null} curl -X PUT 'http://localhost:3333/api/kiwi/file?path=concepts/auth.md' \ -H "Content-Type: text/markdown" \ -H "X-Actor: agent:docs-writer" \ -d '# Authentication OAuth2 + JWT based authentication system.' ``` ```bash Update with optimistic locking theme={null} curl -X PUT 'http://localhost:3333/api/kiwi/file?path=concepts/auth.md' \ -H "Content-Type: text/markdown" \ -H "X-Actor: agent:docs-writer" \ -H "If-Match: a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2" \ -d '# Authentication (updated) Revised content here.' ``` If you pass `If-Match` and the file has been modified since your read, the server returns `409 Conflict` with an error message. Fetch the latest version, merge your changes, and retry. ## Delete a file Delete a file and create a git commit recording the deletion. File path to delete. ```bash theme={null} curl -X DELETE 'http://localhost:3333/api/kiwi/file?path=concepts/auth.md' \ -H "X-Actor: agent:cleanup" ``` ## Bulk write Write multiple files in a single git commit. This is more efficient than individual writes and keeps your git history clean. Git commit author attribution. ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/bulk' \ -H "Content-Type: application/json" \ -H "X-Actor: agent:importer" \ -d '{ "files": [ {"path": "concepts/auth.md", "content": "# Authentication\n\nOAuth2 flow."}, {"path": "concepts/billing.md", "content": "# Billing\n\nStripe integration."}, {"path": "concepts/users.md", "content": "# Users\n\nUser management."} ] }' ``` Use bulk writes when importing data or making batch updates. All files are committed atomically in a single git commit. ## Upload assets Upload binary files (images, PDFs, etc.) as multipart form data. ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/assets' \ -F "file=@diagram.png" \ -H "X-Actor: agent:uploader" ``` The response returns the asset path you can reference in markdown: ```json theme={null} {"path": "assets/diagram.png"} ``` ## Table of contents Get the heading outline for a file. File path to extract headings from. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/toc?path=concepts/auth.md' ``` ```json theme={null} [ {"level": 1, "text": "Authentication", "slug": "authentication"}, {"level": 2, "text": "OAuth2 flow", "slug": "oauth2-flow"}, {"level": 2, "text": "JWT tokens", "slug": "jwt-tokens"} ] ``` ## Append to a file Append content to an existing file without reading it first. ```http theme={null} POST /api/kiwi/file/append ``` File path to append to. String inserted between existing content and new content. ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/file/append?path=log/agent.md' \ -H "X-Actor: agent:logger" \ -d '## Entry 42 New finding from run 249.' ``` ```json theme={null} {"path": "log/agent.md", "etag": "b2c3d4e5f6..."} ``` Append is useful for agent logs, episodic memory, and audit trails where you want to add content without read-modify-write cycles. ## Rename a file Rename or move a file. Wiki links pointing to the old path are automatically updated. ```http theme={null} POST /api/kiwi/rename ``` Old file path. New file path. Rewrite wiki links in other files that point to the old path. ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/rename' \ -H "Content-Type: application/json" \ -d '{"from": "concepts/auth.md", "to": "concepts/authentication.md"}' ``` ```json theme={null} { "from": "concepts/auth.md", "to": "concepts/authentication.md", "etag": "c3d4e5f6...", "updated_links": 8 } ``` ## Rename a directory Move an entire directory and update all internal links. ```http theme={null} POST /api/kiwi/rename-dir ``` Old directory path. New directory path. ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/rename-dir' \ -H "Content-Type: application/json" \ -d '{"from": "concepts", "to": "docs/concepts"}' ``` ```json theme={null} {"from": "concepts", "to": "docs/concepts", "renamed": 15} ``` ## Templates List and read page templates stored in `.kiwi/templates/`. ### List templates ```bash theme={null} curl 'http://localhost:3333/api/kiwi/templates' ``` ```json theme={null} ["runbook", "decision", "episode", "concept"] ``` ### Read a template ```bash theme={null} curl 'http://localhost:3333/api/kiwi/templates?name=runbook' ``` ```json theme={null} {"name": "runbook", "content": "---\ntype: runbook\ntitle: \nstatus: draft\n---\n\n# Title\n\n## Steps\n\n1. ..."} ``` ## Resolve wiki links Resolve `[[wiki-links]]` to their canonical file paths and permalinks. ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/resolve-links' \ -H "Content-Type: application/json" \ -d '{"links": ["auth", "billing", "nonexistent"]}' ``` ```json theme={null} { "resolved": { "auth": {"path": "concepts/auth.md", "title": "Authentication"}, "billing": {"path": "concepts/billing.md", "title": "Billing"} }, "unresolved": ["nonexistent"] } ``` # Import and export Source: https://docs.kiwifs.com/api/import-export REST import (including browse, preview, saved connections, and file ingest) and export formats. ## Import Bulk import data into your knowledge base. The import endpoint accepts the same source configuration options as the `kiwifs import` CLI command. ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/import' \ -H "Content-Type: application/json" \ -H "X-Actor: agent:importer" \ -d '{ "source": "postgres", "connection": "postgres://user:pass@localhost:5432/mydb", "query": "SELECT id, title, body FROM articles WHERE updated_at > '\''2026-01-01'\''", "mapping": { "path": "articles/{{id}}.md", "title": "{{title}}", "content": "{{body}}" } }' ``` ```json theme={null} {"imported": 42, "skipped": 0, "archived": 3, "errors": []} ``` Pages created or updated. Rows unchanged since last import. Pages tombstoned with `_archived_at` during a full sync (when source rows disappear). Per-row error messages. | Source | Config value | Description | | ------------- | --------------- | ----------------------------------- | | PostgreSQL | `postgres` | Import from PostgreSQL queries | | MySQL | `mysql` | Import from MySQL queries | | MongoDB | `mongodb` | Import from MongoDB collections | | SQLite | `sqlite` | Import from SQLite databases | | CSV | `csv` | Import from CSV files | | JSON | `json` | Import from JSON or JSONL files | | Notion | `notion` | Import from Notion workspace | | Airtable | `airtable` | Import from Airtable bases | | Google Sheets | `gsheets` | Import from Google Sheets | | Confluence | `confluence` | Import from Confluence spaces | | Obsidian | `obsidian` | Import from Obsidian vaults | | DynamoDB | `dynamodb` | Import from DynamoDB tables | | Redis | `redis` | Import from Redis keys | | Elasticsearch | `elasticsearch` | Import from Elasticsearch indices | | Firestore | `firestore` | Import from Google Firestore | | YAML | `yaml` | Import from YAML files | | Excel | `excel` | Import from `.xlsx` spreadsheets | | Markdown | `markdown` | Import from a folder of `.md` files | Use double-brace templates to map source fields to KiwiFS file paths and frontmatter: ```json theme={null} { "path": "concepts/{{slug}}.md", "title": "{{name}}", "content": "{{body}}", "frontmatter": { "source": "postgres", "source_id": "{{id}}", "status": "draft" } } ``` Large imports can take time. The endpoint responds synchronously. For very large datasets, consider using the CLI `kiwifs import` command with progress output. ## Upload file for import Upload a file to the server for import processing. ```http theme={null} POST /import/upload Content-Type: multipart/form-data ``` The uploaded file is stored temporarily and can be referenced by subsequent `POST /import` calls. ## List supported sources ```http theme={null} GET /import/sources ``` Returns the list of supported import source types and their required configuration fields. ```json theme={null} ["postgres", "mysql", "sqlite", "mongodb", "firestore", "dynamodb", "redis", "elasticsearch", "csv", "json", "jsonl", "yaml", "excel", "notion", "airtable", "gsheets", "obsidian", "confluence", "markdown"] ``` ## Import browse List **tables or collections** for a source before you run a full import. Same base URL prefix: `http://localhost:3333/api/kiwi`. ```http theme={null} POST /import/browse Content-Type: application/json ``` Body fields depend on `from` (same family as `POST /import`): | `from` | Required fields (typical) | | ----------- | -------------------------------------- | | `postgres` | `dsn` | | `mysql` | `dsn` | | `mongodb` | `uri` (or `dsn`), `database` | | `firestore` | `project`, optional `credentials` JSON | ```json theme={null} { "from": "postgres", "dsn": "postgres://user:pass@localhost:5432/db" } ``` ```json theme={null} { "tables": [{ "name": "articles" }, { "name": "comments" }] } ``` Unsupported `from` values return **`400`** when browse is not implemented for that source. ## Import preview Fetch sample rows **without writing** pages. Uses the same JSON shape as `POST /import`, plus optional `limit` (default **5**, max **20**). ```http theme={null} POST /import/preview Content-Type: application/json ``` ```json theme={null} { "records": [ { "path": "postgres/acme.md", "frontmatter": { "_source": "postgres", "_source_id": "...", "title": "Acme" }, "body_preview": "# Acme\n\n> Auto-imported from postgres (row ...)" } ] } ``` ## Import connections When a connection store is enabled, the server persists **metadata only** (never secrets). Re-runs require credentials in the request body. | Method | Path | Purpose | | -------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GET` | `/import/connections` | List saved connections (empty array if disabled). | | `POST` | `/import/connections` | Save or update metadata (`from`, `name`, `dsn`, `uri`, `table`, `collection`, `database`, `database_id`, `base_id`, `table_id`, `project`, `prefix`, `id_column`, `columns`, last-run stats, etc.). | | `DELETE` | `/import/connections/:id` | Remove a connection. **`204`**. | | `POST` | `/import/connections/:id/run` | Run import again. Body may include `credentials` and/or `api_key` for that run only. Response matches `POST /import`: `{ "imported", "skipped", "archived", "errors" }`. | | `POST` | `/import/connections/:id/sync` | Enable, pause, or change auto-sync interval. Body: `{ "enabled": true, "interval": "1h" }`. | ## Ingest from files Convert supported office and document formats to markdown (MarkItDown pipeline). ```http theme={null} POST /ingest Content-Type: application/json ``` ```json theme={null} { "file": "/tmp/report.pdf", "split_mode": "single", "prefix": "imports/", "extract_keywords": true, "max_keywords": 10, "convert_crossrefs": false, "actor": "ingest-bot" } ``` * `file` must use an extension the server accepts for MarkItDown. * `split_mode` defaults to `single`. ## Export Export your knowledge base in structured formats. Output format: `jsonl`, `csv`, or `parquet`. Scope export to a subdirectory (e.g. `concepts`). Comma-separated frontmatter fields to include as columns. Include the full markdown body in the export. Include outgoing wiki links for each page. Include vector embeddings (if available). Maximum number of pages to export. ```bash Export as JSONL theme={null} curl 'http://localhost:3333/api/kiwi/export?format=jsonl&path=concepts&include-content=true' ``` ```bash Export as CSV theme={null} curl 'http://localhost:3333/api/kiwi/export?format=csv&columns=title,status,updated&path=concepts' ``` ```bash Export with links and embeddings theme={null} curl 'http://localhost:3333/api/kiwi/export?format=jsonl&include-content=true&include-links=true&include-embeddings=true' ``` ### JSONL output Each line is a JSON object representing one page: ```json theme={null} {"path":"concepts/auth.md","title":"Authentication","status":"verified","content":"# Authentication\n\nOAuth2 + JWT...","links":["users","billing"]} {"path":"concepts/billing.md","title":"Billing","status":"draft","content":"# Billing\n\nStripe integration...","links":["users"]} ``` ### CSV output ```csv theme={null} path,title,status,updated concepts/auth.md,Authentication,verified,2026-04-25 concepts/billing.md,Billing,draft,2026-04-20 ``` Use JSONL export with `include-content=true` to create backups or to feed your knowledge base into external pipelines. ## Document export Render markdown to PDF, HTML, slides, or a static site: ```http theme={null} POST /export/document Content-Type: application/json ``` See [Document export](/export/documents) for the full request body and examples. ## Airbyte endpoints | Method | Path | | ------ | ----------------------------------- | | `POST` | `/import/airbyte/spec` | | `POST` | `/import/airbyte/check` | | `POST` | `/import/airbyte/discover` | | `POST` | `/import/airbyte-cloud/check` | | `POST` | `/import/airbyte-cloud/discover` | | `GET` | `/import/airbyte-cloud/connections` | | `POST` | `/import/airbyte-cloud/sync` | | `GET` | `/import/sync/status` | See [Airbyte import](/import/airbyte). # Metadata and graph Source: https://docs.kiwifs.com/api/metadata DQL queries, aggregations, backlinks, and knowledge graph endpoints. KiwiFS treats frontmatter as structured metadata you can query, aggregate, and graph. The metadata API provides a DataView Query Language (DQL) engine, SQL-style aggregations, and a full wiki-link graph. ## DQL queries Query pages using DataView Query Language. DQL operates over frontmatter fields and supports multiple output modes. DQL query string. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/query?q=TABLE+title,+status+FROM+"concepts"+WHERE+status+=+"draft"' ``` ```json theme={null} { "mode": "TABLE", "headers": ["path", "title", "status"], "rows": [ ["concepts/billing.md", "Billing", "draft"], ["concepts/users.md", "Users", "draft"] ] } ``` ### Query modes | Mode | Description | Example | | ---------- | ------------------------------------ | ------------------------------------------------ | | `TABLE` | Tabular results with selected fields | `TABLE title, status FROM "concepts"` | | `LIST` | Flat list of matching page paths | `LIST FROM "concepts" WHERE status = "verified"` | | `COUNT` | Count of matching pages | `COUNT FROM "concepts" WHERE status = "draft"` | | `DISTINCT` | Unique values for a field | `DISTINCT status FROM "concepts"` | ```bash Table query theme={null} curl 'http://localhost:3333/api/kiwi/query?q=TABLE+title,+status+FROM+"concepts"+WHERE+status+=+"draft"' ``` ```bash Count query theme={null} curl 'http://localhost:3333/api/kiwi/query?q=COUNT+FROM+"concepts"+WHERE+status+=+"draft"' ``` ```bash Distinct values theme={null} curl 'http://localhost:3333/api/kiwi/query?q=DISTINCT+status+FROM+"concepts"' ``` ## Aggregations SQL-style aggregation queries over frontmatter fields. Frontmatter field to group by. Comma-separated aggregation functions. Format: `function` or `function:field`. Supported functions: `count`, `avg`, `sum`, `min`, `max`. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/query/aggregate?group=status&calc=count,avg:priority' ``` ```json theme={null} [ {"status": "draft", "count": 12, "avg_priority": 2.3}, {"status": "verified", "count": 45, "avg_priority": 1.8}, {"status": "stale", "count": 3, "avg_priority": 3.0} ] ``` ## Refresh computed views Trigger a refresh of all computed views (materialized DQL queries). ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/view/refresh' ``` ```json theme={null} {"refreshed": 4, "duration_ms": 120} ``` Views refresh automatically on file writes. Use this endpoint to force a manual refresh after bulk imports or direct filesystem changes. ## Backlinks Get all pages that link to a given page via wiki links. File path to find backlinks for. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/backlinks?path=concepts/auth.md' ``` ```json theme={null} [ {"path": "concepts/users.md", "title": "Users", "context": "...uses [[auth]] for login..."}, {"path": "concepts/api-keys.md", "title": "API Keys", "context": "...see [[auth]] for details..."} ] ``` ## Knowledge graph Retrieve the full wiki-link graph as JSON. This powers the graph visualization in the web UI. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/graph' ``` ```json theme={null} { "nodes": [ {"id": "concepts/auth.md", "title": "Authentication", "group": "concepts"}, {"id": "concepts/users.md", "title": "Users", "group": "concepts"}, {"id": "concepts/billing.md", "title": "Billing", "group": "concepts"} ], "edges": [ {"source": "concepts/users.md", "target": "concepts/auth.md"}, {"source": "concepts/billing.md", "target": "concepts/users.md"} ] } ``` Use the graph endpoint to build custom visualizations or to analyze the connectivity of your knowledge base. Orphan pages (nodes with no edges) are candidates for linking or removal. # API overview Source: https://docs.kiwifs.com/api/overview Base URL, authentication, common headers, and error handling for the KiwiFS REST API. The KiwiFS REST API gives you full read/write access to your markdown filesystem over HTTP. Every operation available in the web UI and MCP interface is also available as a REST endpoint. ## Base URL ``` http://localhost:3333/api/kiwi ``` All endpoints are prefixed with `/api/kiwi/`. In multi-space mode, endpoints include the space name: ``` /api/kiwi/{space}/file?path=concepts/auth.md ``` You can also keep the path **`/api/kiwi/...`** (no `{space}` segment) and send the header **`X-Kiwi-Space: engineering`** so reverse proxies or the FUSE client can dispatch to a configured space without changing URL shapes. Space **administration** (`GET/POST/DELETE /api/spaces`) is documented in [Spaces API](/api/spaces). See [Multi-space](/concepts/multi-space). ## Authentication Configure the auth type in `.kiwi/config.toml` under `[auth] type`. No authentication required. Suitable for local development and trusted networks. ```bash theme={null} curl http://localhost:3333/api/kiwi/tree ``` Pass a global API key via the `Authorization: Bearer` header. ```bash theme={null} curl http://localhost:3333/api/kiwi/tree \ -H "Authorization: Bearer kiwi_sk_abc123" ``` Each space has its own API key. Use the same header format as `apikey` mode. ```bash theme={null} curl http://localhost:3333/api/kiwi/engineering/tree \ -H "Authorization: Bearer kiwi_sk_eng_xyz789" ``` OpenID Connect authentication. Pass a JWT Bearer token issued by your identity provider. ```bash theme={null} curl http://localhost:3333/api/kiwi/tree \ -H "Authorization: Bearer eyJhbGciOi..." ``` ## Common headers | Header | Purpose | Example | | -------------- | --------------------------------------------------------- | ------------------------------------- | | `X-Actor` | Attribution for the git commit author | `agent:my-agent` | | `X-Provenance` | Lineage tracking for audit trails | `run:run-249` | | `If-Match` | Optimistic locking using ETag (git blob SHA) | `a1b2c3d4e5f6` | | `Content-Type` | Body format | `text/markdown` or `application/json` | | `X-Kiwi-Space` | Target space when using the shared `/api/kiwi/...` prefix | `engineering` | | `X-Space` | Recorded on audit log entries when present | `engineering` | Always set `X-Actor` on write operations. KiwiFS uses this value as the git commit author, making it easy to trace which agent or user made each change. ## Error handling All errors return a JSON body with a single `error` field and a standard HTTP status code. ```json theme={null} {"error": "file not found: concepts/missing.md"} ``` | Status code | Meaning | | ----------- | --------------------------------------------------------------------------------------------- | | `400` | Bad request (malformed query, missing required field) | | `401` | Unauthorized (missing or invalid credentials) | | `404` | Resource not found | | `409` | Conflict (ETag mismatch on optimistic lock, or draft merge failure) | | `422` | Unprocessable entity (validation error) | | `501` | Not implemented (optional subsystem disabled, for example drafts) | | `503` | Service unavailable (claims, vector search, Dataview executor, and similar optional services) | | `500` | Internal server error | ## Health endpoints Use these endpoints for liveness and readiness probes in container orchestrators. ```bash Health theme={null} curl http://localhost:3333/health ``` ```bash Liveness theme={null} curl http://localhost:3333/healthz ``` ```bash Readiness theme={null} curl http://localhost:3333/readyz ``` All three return JSON. **`/health`** and **`/healthz`** always respond with **`200`** when the process is up: ```json theme={null} {"status": "ok"} ``` **`/readyz`** checks storage and optional protocol health (NFS, S3, WebDAV). When storage is unreachable or an enabled protocol is unhealthy, it returns **`503`**: ```json theme={null} { "status": "ok", "protocols": { "nfs": { "enabled": true, "healthy": true, "port": 2049 }, "s3": { "enabled": false }, "webdav": { "enabled": true, "healthy": false, "error": "listen tcp :3335: bind: address already in use" } } } ``` Use `/readyz` for Kubernetes readiness probes when optional protocols are enabled. ## Prometheus metrics ```bash theme={null} curl http://localhost:3333/metrics ``` Exposes Prometheus-formatted metrics including request counts, latencies, and active connections. Use this to wire KiwiFS into your monitoring stack (Grafana, Datadog, etc.). ## Rate limiting When `[server.rate_limit]` in `.kiwi/config.toml` sets `requests_per_minute` greater than zero, KiwiFS applies a token-bucket limiter keyed by **Bearer token hash** (or client IP when anonymous). Health, readiness, and metrics routes skip the limiter. If `requests_per_minute` is **not** configured, no built-in HTTP rate limit is applied — use a reverse proxy (nginx, Caddy, Envoy) for strict quotas or geographic rules. ## Machine-readable references KiwiFS provides machine-readable documentation for agents and integrations: | Resource | URL | | ----------------- | ---------------------------------------------------------------------- | | **llms.txt** | [docs.kiwifs.com/llms.txt](https://docs.kiwifs.com/llms.txt) | | **llms-full.txt** | [docs.kiwifs.com/llms-full.txt](https://docs.kiwifs.com/llms-full.txt) | | **OpenAPI spec** | `GET /api/openapi.json` on any KiwiFS instance | | **Swagger UI** | `GET /api/docs` on any KiwiFS instance | Point your agent at `llms-full.txt` for a single-file reference of every REST endpoint, MCP tool, and CLI command. The OpenAPI spec at `/api/openapi.json` is machine-parseable for code generation. ## Next steps Read, write, and delete files. Full-text and vector search. Git history, diff, and blame. Structured queries and knowledge graph. Staging branches for agents. Saved DQL bases. Lint, timeline, graph helpers, rules, audit. # Publish Source: https://docs.kiwifs.com/api/publish Publish and unpublish pages via frontmatter and read them at /p/{path}. Publishing marks a page with `published: true` and `published_at` in frontmatter. Published pages are readable at `/p/{path}` without API authentication. ## Publish a page ```http theme={null} POST /api/kiwi/publish Content-Type: application/json ``` File path to publish. ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/publish' \ -H 'Content-Type: application/json' \ -H 'X-Actor: agent:publisher' \ -d '{"path":"concepts/auth.md"}' ``` ```json Response theme={null} { "path": "concepts/auth.md", "published": true, "published_at": "2026-05-02T14:30:00Z", "public_url": "/p/concepts/auth.md" } ``` `published_at` is set only on first publish. Later republish calls preserve the original timestamp. ## Bulk publish Publish multiple pages in a single request. ```http theme={null} POST /api/kiwi/publish/bulk Content-Type: application/json ``` Array of file paths to publish. ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/publish/bulk' \ -H 'Content-Type: application/json' \ -H 'X-Actor: agent:publisher' \ -d '{"paths":["concepts/auth.md","concepts/billing.md","concepts/users.md"]}' ``` ```json Response theme={null} { "published": 3, "results": [ {"path": "concepts/auth.md", "published": true}, {"path": "concepts/billing.md", "published": true}, {"path": "concepts/users.md", "published": true} ] } ``` ## Unpublish ```http theme={null} POST /api/kiwi/unpublish Content-Type: application/json ``` File path to unpublish. Sets `published: false` but keeps `published_at` for history. ## Bulk unpublish ```http theme={null} POST /api/kiwi/unpublish/bulk Content-Type: application/json ``` Array of file paths to unpublish. ## List published pages ```http theme={null} GET /api/kiwi/publish/list ``` Returns all currently published pages. ```json Response theme={null} { "pages": [ {"path": "concepts/auth.md", "published_at": "2026-05-02T14:30:00Z", "public_url": "/p/concepts/auth.md"}, {"path": "guides/quickstart.md", "published_at": "2026-05-01T10:00:00Z", "public_url": "/p/guides/quickstart.md"} ] } ``` ## Publish status ```http theme={null} GET /api/kiwi/publish/status?path=concepts/auth.md ``` File path to check. ```json Response theme={null} { "path": "concepts/auth.md", "published": true, "published_at": "2026-05-02T14:30:00Z", "public_url": "/p/concepts/auth.md", "view_count": 42 } ``` Included when publish metrics tracking is enabled on the server. ## Read published pages No API key required: ```bash theme={null} curl 'http://localhost:3333/p/concepts/auth.md' ``` ## Public visibility (alternative model) Separate from per-page publish flags, mark pages with `visibility: public` in frontmatter: ```bash Read public file theme={null} curl 'http://localhost:3333/api/kiwi/public/file?path=concepts/open.md' ``` ```bash Browse public tree theme={null} curl 'http://localhost:3333/api/kiwi/public/tree?path=' ``` Non-public pages return `404` to avoid leaking existence. ## Related documentation Publish toggle in the page header. Share links and public routes. # Schemas Source: https://docs.kiwifs.com/api/schemas Manage JSON Schema definitions for frontmatter validation. Enable enforcement with `[schema] enforce = true` in config. See [Schema Validation](/concepts/schemas) for the full concept. ## List schema types ```http theme={null} GET /api/kiwi/schemas ``` ```bash theme={null} curl 'http://localhost:3333/api/kiwi/schemas' ``` ```json theme={null} ["runbook", "concept", "episode"] ``` ## Get a schema ```http theme={null} GET /api/kiwi/schemas/:type ``` Schema type name (matches the frontmatter `type` field). ```bash theme={null} curl 'http://localhost:3333/api/kiwi/schemas/runbook' ``` Returns the raw JSON Schema document. ## Create or update a schema ```http theme={null} PUT /api/kiwi/schemas/:type ``` Schema type name. The request body is a raw JSON Schema document. ```bash theme={null} curl -X PUT 'http://localhost:3333/api/kiwi/schemas/runbook' \ -H "Content-Type: application/json" \ -d '{ "type": "object", "required": ["title", "status", "owner"], "properties": { "title": {"type": "string"}, "status": {"type": "string", "enum": ["draft", "reviewed", "verified"]}, "owner": {"type": "string"} } }' ``` When `[schema] enforce = true`, schema changes trigger a reload so subsequent writes use the updated schema immediately. # Search Source: https://docs.kiwifs.com/api/search Full-text, trust-ranked, vector, and metadata search endpoints. KiwiFS provides multiple search strategies. You can use full-text search for keyword matching, vector search for semantic similarity, or metadata queries for structured filtering. ## Full-text search BM25-ranked full-text search powered by SQLite FTS5. Search query. Supports boolean operators and phrase matching. Similarity threshold (0–1) for "did you mean?" suggestions when the query yields few results. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/search?q=payment+timeout' ``` ```json theme={null} [ { "path": "concepts/payments.md", "title": "Payments", "snippet": "...handles payment retries after a timeout...", "score": 4.82 } ] ``` ### Query syntax | Syntax | Example | Behavior | | ----------- | --------------------- | -------------------------------------- | | Keywords | `payment timeout` | Match pages containing both terms | | Boolean AND | `payment AND timeout` | Explicit AND (same as space-separated) | | Boolean OR | `payment OR billing` | Match pages containing either term | | Boolean NOT | `payment NOT refund` | Exclude pages containing a term | | Phrase | `"payment timeout"` | Match the exact phrase | ```bash Boolean search theme={null} curl 'http://localhost:3333/api/kiwi/search?q=payment+AND+timeout' ``` ```bash Phrase search theme={null} curl 'http://localhost:3333/api/kiwi/search?q=%22payment+timeout%22' ``` ```bash Exclusion theme={null} curl 'http://localhost:3333/api/kiwi/search?q=payment+NOT+refund' ``` ## Trust-ranked search Search results weighted by trust signals in frontmatter. Pages marked as verified or source-of-truth rank higher. Search query (same syntax as full-text search). ```bash theme={null} curl 'http://localhost:3333/api/kiwi/search/verified?q=auth' ``` ```json theme={null} [ { "path": "concepts/auth.md", "title": "Authentication", "snippet": "...OAuth2 auth flow...", "score": 8.91, "trust": {"status": "verified", "source-of-truth": true} } ] ``` Add `status: verified` or `source-of-truth: true` to your page frontmatter to boost that page in trust-ranked results. ## Semantic search Vector similarity search using embeddings. Requires a vector search provider configured in `.kiwi/config.toml`. Natural language query. Maximum number of results to return. ```bash POST theme={null} curl -X POST 'http://localhost:3333/api/kiwi/search/semantic' \ -H "Content-Type: application/json" \ -d '{"query": "how does auth work?", "limit": 10}' ``` ```bash GET theme={null} curl 'http://localhost:3333/api/kiwi/search/semantic?query=how+does+auth+work&limit=10' ``` ```json theme={null} [ { "path": "concepts/auth.md", "title": "Authentication", "snippet": "OAuth2 + JWT based authentication system...", "similarity": 0.92 } ] ``` Semantic search requires vector embeddings. Configure an embedder provider (OpenAI, Ollama, Cohere, etc.) in your `.kiwi/config.toml` under `[search.vector]`. ## Metadata query Query pages by their frontmatter fields using JSON path syntax. JSON path filter expression (e.g. `$.status=draft`). JSON path to the field to sort by (e.g. `$.updated`). Sort order: `asc` or `desc`. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/meta?where=$.status=draft&sort=$.updated&order=desc' ``` ```json theme={null} [ { "path": "concepts/billing.md", "title": "Billing", "metadata": {"status": "draft", "updated": "2026-04-20", "author": "agent:writer"} } ] ``` Combine metadata queries with the [DQL endpoint](/api/metadata) for more complex structured queries across your knowledge base. # Spaces admin Source: https://docs.kiwifs.com/api/spaces List, create, and remove knowledge spaces on a multi-space KiwiFS server. When KiwiFS runs with a **space manager**, space administration lives at `/api/spaces` (not under `/api/kiwi/`). Per-space file APIs remain at `/api/kiwi/...` or `/api/kiwi/{space}/...`. See [Multi-space](/concepts/multi-space) for routing rules. ## List spaces ```http theme={null} GET /api/spaces ``` ```bash theme={null} curl -s 'http://localhost:3333/api/spaces' ``` ```json Response theme={null} { "spaces": [ { "name": "engineering", "root": "/data/eng-wiki", "fileCount": 1420, "sizeBytes": 45000000, "lastModified": "2026-05-02T10:00:00Z" } ] } ``` Unique space identifier. Filesystem path for this space. Number of markdown files. Total size on disk. Most recent file modification. ## Get one space ```http theme={null} GET /api/spaces/:name ``` ```bash theme={null} curl -s 'http://localhost:3333/api/spaces/engineering' ``` Returns `404` if the name is unknown. ## List init templates ```http theme={null} GET /api/init-templates ``` ```bash theme={null} curl -s 'http://localhost:3333/api/init-templates' ``` ```json Response theme={null} [ {"id": "knowledge", "name": "Knowledge Base", "description": "LLM-maintained KB with schema, episodes, playbook"}, {"id": "wiki", "name": "Wiki", "description": "Team wiki with decisions, processes, reference docs"}, {"id": "runbook", "name": "Runbook", "description": "Ops procedures, incidents, postmortems"}, {"id": "research", "name": "Research", "description": "Notes, experiments, literature"}, {"id": "tasks", "name": "Tasks", "description": "Task tracking with workflow schema"}, {"id": "blank", "name": "Blank", "description": "Config only, no starter files"} ] ``` Use the template `id` when creating a space with `POST /api/spaces`. ## Create a space ```http theme={null} POST /api/spaces Content-Type: application/json ``` Unique identifier for the new space. Filesystem path for the space's knowledge root. Init template to scaffold the space with (e.g. `knowledge`, `wiki`, `tasks`). Use `GET /api/init-templates` to list available templates. ```bash theme={null} curl -X POST 'http://localhost:3333/api/spaces' \ -H 'Content-Type: application/json' \ -d '{"name":"research","root":"/data/research-wiki","template":"research"}' ``` Returns `201` with space metadata. Returns `409` if the name already exists. KiwiFS creates the root directory, initializes git and indexes, and persists the entry to `.kiwi/spaces.json`. ## Delete a space ```http theme={null} DELETE /api/spaces/:name ``` ```bash theme={null} curl -X DELETE 'http://localhost:3333/api/spaces/research' ``` ```json Response theme={null} { "deleted": "research" } ``` Removes the space from the manager but does **not** delete files on disk. Clean up the `root` directory separately if needed. ## Auth Protect `/api/spaces` in production. Creation and deletion are administrative operations. Per-space API keys (`auth.type = perspace`) apply to `/api/kiwi` routes after routing. ## Related documentation Routing and configuration. `[[spaces]]` config syntax. # Utilities and agent helpers Source: https://docs.kiwifs.com/api/utilities Symlinks, linting, peek and section reads, timeline and feeds, clipping, extended graph APIs, rules, space settings, audit logs, and public share URLs. These endpoints share the usual prefix `http://localhost:3333/api/kiwi` and authentication rules from [API overview](/api/overview). ## Read symlink target ```http theme={null} GET /readlink?path=link/to/page.md ``` Returns **plain text** target path. **`404`** if the path is missing; **`400`** if the path is not a symlink. ## Lint markdown ```http theme={null} POST /lint Content-Type: application/json ``` Lint an existing file: ```json theme={null} { "path": "concepts/auth.md" } ``` Or inline content: ```json theme={null} { "content": "# Title\n\nBody..." } ``` **Response:** `{ "issues": [ /* structured lint records */ ] }` (always an array, possibly empty). ## Peek summary ```http theme={null} GET /peek?path=concepts/auth.md ``` Returns JSON with `title`, `snippet`, `frontmatter`, `links_out`, `links_in`, `headings`, `word_count`, and other lightweight fields without returning the full body. ## Read one section ```http theme={null} GET /section?path=concepts/auth.md&heading=OAuth2%20flow ``` or ```http theme={null} GET /section?path=concepts/auth.md&index=2 ``` Returns `{ "path", "heading", "level", "content", "line_start", "line_end" }`. ## Activity timeline ```http theme={null} GET /timeline?limit=50&offset=0&actor=agent:bot&type=write&path_prefix=runbooks/ ``` * `type` filter: `write`, `delete`, or omit for both. * `limit` defaults to **50**, max **500**. **Response:** `{ "events": [...], "total": }` — `total` is a hint for pagination (see server implementation). ## Feeds ```http theme={null} GET /feed.xml GET /feed.json ``` Atom and JSON Feed built from recent timeline events (titles include updated vs deleted paths). ## Clip a web page ```http theme={null} POST /clip Content-Type: application/json ``` ```json theme={null} { "url": "https://example.com/article", "title": "Optional override", "tags": ["research", "web"], "folder": "clips/" } ``` Fetches the article, converts to markdown, writes through the pipeline using `X-Actor` (default `clipper`), and returns `{ "path", "title", "excerpt" }`. ## Extended graph APIs All of the following return **`503`** when link indexing is unavailable. ### Centrality ```http theme={null} GET /graph/centrality?limit=50 ``` Returns `{ "pages": [ { "path", "pagerank", "betweenness", ... } ] }` (truncated to `limit`). ### Communities ```http theme={null} GET /graph/communities ``` Louvain communities over wiki-link edges: `{ "communities": ... }`. ### Shortest path ```http theme={null} GET /graph/path?from=concepts/a.md&to=runbooks/b.md ``` Returns `{ "path": ["concepts/a.md", "..."] }`. **`404`** when no path exists. ### Local neighborhood ```http theme={null} GET /graph/walk?path=concepts/a.md&include_siblings=true ``` Returns outbound wiki links, backlinks, and optional same-directory neighbors. ## Page templates Slash-command templates in `.kiwi/templates/`: ```http theme={null} GET /templates GET /template?name=meeting-notes ``` `GET /templates` lists available template names. `GET /template` returns the template body for insertion into new pages. ## Search evaluation Benchmark search quality against expected paths: ```http theme={null} POST /eval Content-Type: application/json ``` ```json theme={null} { "queries": [ { "question": "authentication flow", "expected_paths": ["pages/auth.md", "concepts/oauth.md"] } ] } ``` Returns per-query FTS and semantic ranks plus aggregate `hit_rate`, `mrr`, and `precision_at_5`. MCP: **`kiwi_eval`**. ## Backup status When `[backup]` is configured: ```http theme={null} GET /backup/status GET /sync/status ``` Returns last push time, remote URL, and error state for the background backup goroutine. ## Rules file ```http theme={null} GET /rules GET /rules?format=cursor PUT /rules ``` * Without `format`, returns raw `.kiwi/rules.md` (empty string when missing). * `format` may be `cursor`, `claude`, `agents`, or `openclaw` to wrap user rules for a specific harness. * `PUT` accepts raw markdown (max **256 KiB**) and commits `.kiwi/rules.md` when git versioning is enabled. ## Space metadata ```http theme={null} GET /space/info ``` ```json theme={null} { "visibility": "private", "root": "/data/knowledge" } ``` ```http theme={null} PUT /space/visibility Content-Type: application/json ``` ```json theme={null} { "visibility": "public" } ``` Allowed values: `private`, `unlisted`, `public`. Persists into `.kiwi/config.toml` and updates the live config. ## Audit log ```http theme={null} GET /audit?since=2026-05-01T00:00:00Z&limit=100 ``` Returns JSON array entries `{ "ts", "method", "path", "space", "actor", "token_hash", "ip", "status", "duration_ms" }`. **`501`** when audit logging was not enabled for this server. Default `since` is the last 24 hours; `limit` defaults to **100** (max **10000**). ## Public routes ### Share tokens Create links with [`POST /share`](/api/analytics#sharing). Readers call **without** API authentication: ```http theme={null} GET /api/kiwi/public/:token ``` Optional password: query `password` or header `X-Share-Password`. Wrong or missing passwords return **`401`** with a `WWW-Authenticate` challenge when the link is password protected. ### Public visibility browsing These endpoints expose only markdown that declares **`visibility: public`** in frontmatter: ```http theme={null} GET /api/kiwi/public/file?path=concepts/open.md GET /api/kiwi/public/tree?path= ``` Non-public pages return **`404`** as "not found" to avoid leaking existence. ## Raw files ```http theme={null} GET /raw/* ``` Serves raw bytes for static assets and markdown outside the JSON APIs (used by the web UI). Not a substitute for `GET /file` when you need `ETag` semantics. ## UI configuration ```http theme={null} GET /ui-config ``` Returns JSON consumed by the embedded web UI (feature flags, layout hints). ## MCP parity Many of these flows have MCP tools (`kiwi_lint`, `kiwi_peek`, `kiwi_section`, `kiwi_timeline`, `kiwi_feed`, `kiwi_clip`, `kiwi_graph_*`, and more). See [MCP](/concepts/mcp). # Velocity and graph analytics Source: https://docs.kiwifs.com/api/velocity Change velocity, PageRank, and structural analysis of your knowledge base. ## Change velocity Analyze the rate and distribution of changes across your knowledge base over a time period. ```http theme={null} GET /api/kiwi/velocity ``` Time window. Supports `Nd` (days), `Nw` (weeks), `Nm` (months). Max number of items in hot/cold spot lists. Limit analysis to a subdirectory. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/velocity?period=14d&limit=5' ``` ```json theme={null} { "period": "14d", "total_changes": 87, "hot_spots": [ {"path": "concepts/auth.md", "changes": 12}, {"path": "runbooks/deploy.md", "changes": 9} ], "cold_spots": [ {"path": "concepts/legacy.md", "changes": 0, "days_since_update": 120} ], "bursts": [ {"date": "2026-04-28", "changes": 23, "actor": "agent:importer"} ], "single_author_pages": 14 } ``` Total commits in the period. Most frequently changed pages. Least recently changed pages. Days with unusually high change counts. Pages with only one contributor. Use velocity data to identify knowledge that's churning (hot spots may need stabilization) and knowledge that's stale (cold spots may need review). *** ## Graph analytics Structural analysis of the wiki-link graph using PageRank and connectivity metrics. ```http theme={null} GET /api/kiwi/graph/analytics ``` Number of top pages to return. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/graph/analytics?limit=10' ``` ```json theme={null} { "total_nodes": 142, "total_edges": 387, "components": 3, "largest_component_size": 138, "orphans": ["concepts/legacy.md", "scratch/notes.md"], "top_pages": [ {"path": "concepts/auth.md", "pagerank": 0.042, "in_degree": 18, "out_degree": 5}, {"path": "concepts/users.md", "pagerank": 0.038, "in_degree": 15, "out_degree": 8} ] } ``` Total pages in the graph. Total wiki-link connections. Number of disconnected subgraphs. Pages in the largest connected component. Pages with no incoming or outgoing links. Highest PageRank pages with degree counts. Returns `503` if the link indexer is not available. ### Centrality, communities, path, and walk Additional graph helpers live alongside `/graph/analytics`: | Endpoint | Purpose | | ----------------------------------- | ------------------------------------------------------------------- | | `GET /graph/centrality?limit=50` | PageRank and betweenness-style rankings per page. | | `GET /graph/communities` | Louvain community detection over wiki-link edges. | | `GET /graph/path?from=a.md&to=b.md` | Shortest path between two pages. | | `GET /graph/walk?path=a.md` | One-hop neighbors, backlinks, and optional same-directory siblings. | See [Utilities](/api/utilities#extended-graph-apis) for response notes and error codes. *** ## Suggestions Get semantically similar pages for a given page, useful for "related pages" features and discovering missing links. ```http theme={null} GET /api/kiwi/suggestions ``` File path to find suggestions for. Max suggestions to return. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/suggestions?path=concepts/auth.md&limit=3' ``` ```json theme={null} { "suggestions": [ {"target": "concepts/api-keys.md", "similarity": 0.89, "snippet": "API key authentication..."}, {"target": "concepts/sessions.md", "similarity": 0.84, "snippet": "Session management..."} ] } ``` Already-linked pages are filtered out. Requires vector search to be enabled. *** ## Embeddings Retrieve the raw embedding vectors for a page's chunks. ```http theme={null} GET /api/kiwi/embeddings ``` File path to retrieve embeddings for. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/embeddings?path=concepts/auth.md' ``` ```json theme={null} { "path": "concepts/auth.md", "dimensions": 768, "chunks": [ {"chunk_idx": 0, "text": "# Authentication\n\nOAuth2...", "vector": [0.012, -0.034, ...]}, {"chunk_idx": 1, "text": "## Refresh tokens\n\n...", "vector": [0.008, 0.021, ...]} ] } ``` Returns `503` if vector search is not enabled. *** ## Eligible tasks Retrieve ranked actionable tasks inferred from frontmatter metadata — pages with `status` fields indicating they are available for work. ```http theme={null} GET /api/kiwi/eligible ``` ```bash theme={null} curl 'http://localhost:3333/api/kiwi/eligible?limit=10' ``` ```json theme={null} { "tasks": [ {"path": "tasks/t-42.md", "title": "Fix auth timeout", "status": "open", "priority": "high"}, {"path": "tasks/t-15.md", "title": "Update onboarding", "status": "open", "priority": "medium"} ] } ``` Useful for agents that need to pick up the next available task. MCP: **`kiwi_eligible`**. *** ## Search evaluation Benchmark your search quality against expected results. ```http theme={null} POST /api/kiwi/eval ``` ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/eval' \ -H "Content-Type: application/json" \ -d '{ "queries": [ {"question": "how does authentication work", "expected_paths": ["concepts/auth.md"]}, {"question": "deploy checklist", "expected_paths": ["runbooks/deploy.md"]} ] }' ``` ```json theme={null} { "fts": {"hit_rate": 1.0, "mrr": 0.75, "precision_at_5": 0.6}, "semantic": {"hit_rate": 1.0, "mrr": 0.9, "precision_at_5": 0.8}, "per_query": [ {"question": "how does authentication work", "fts_rank": 2, "semantic_rank": 1} ] } ``` Fraction of queries where the expected page appeared in top-5. Mean Reciprocal Rank across all queries. Average precision in the top-5 results. # Versioning Source: https://docs.kiwifs.com/api/versioning Git history, diff, and blame endpoints for tracking changes to your knowledge base. Every write in KiwiFS creates a git commit. The versioning API exposes the full git history for any file, letting you view past versions, compare changes, and trace authorship. ## Version history Get the git log for a specific file. File path relative to the knowledge root. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/versions?path=concepts/auth.md' ``` ```json theme={null} [ { "hash": "e4f5g6h", "author": "agent:docs-writer", "message": "update concepts/auth.md", "timestamp": "2026-04-25T14:30:00Z" }, { "hash": "a1b2c3d", "author": "agent:importer", "message": "create concepts/auth.md", "timestamp": "2026-04-24T09:15:00Z" } ] ``` ## Read a specific version Retrieve the file content at a specific commit. File path relative to the knowledge root. Git commit hash (full or abbreviated). ```bash theme={null} curl 'http://localhost:3333/api/kiwi/version?path=concepts/auth.md&version=a1b2c3d' ``` ``` # Authentication Original content from the first commit... ``` ## Diff between versions Get a unified diff between two commits for a file. File path relative to the knowledge root. Starting commit hash. Ending commit hash. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/diff?path=concepts/auth.md&from=a1b2c3d&to=e4f5g6h' ``` ```diff theme={null} --- a/concepts/auth.md +++ b/concepts/auth.md @@ -1,3 +1,5 @@ # Authentication -Original content from the first commit... +OAuth2 + JWT based authentication system. + +Supports refresh token rotation. ``` ## Blame Get per-line git blame attribution for a file. Each line maps to the commit that last modified it. File path relative to the knowledge root. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/blame?path=concepts/auth.md' ``` ```json theme={null} [ { "line": 1, "content": "# Authentication", "hash": "a1b2c3d", "author": "agent:importer", "timestamp": "2026-04-24T09:15:00Z" }, { "line": 3, "content": "OAuth2 + JWT based authentication system.", "hash": "e4f5g6h", "author": "agent:docs-writer", "timestamp": "2026-04-25T14:30:00Z" } ] ``` Use blame to trace which agent or user wrote each section. The `author` field corresponds to the `X-Actor` header set during the write. ## Change feed Get a paginated feed of all changes across the knowledge base since a given commit. Useful for syncing external systems or building audit logs. ```http theme={null} GET /api/kiwi/changes ``` Git commit hash to start from. Omit to get the most recent changes. Max changes to return (capped at 500). ```bash theme={null} curl 'http://localhost:3333/api/kiwi/changes?limit=10' ``` ```json theme={null} { "changes": [ {"seq": 1, "path": "concepts/auth.md", "action": "write", "actor": "agent:docs-writer", "timestamp": "2026-05-04T12:00:00Z"}, {"seq": 2, "path": "concepts/billing.md", "action": "delete", "actor": "agent:cleanup", "timestamp": "2026-05-04T12:01:00Z"} ], "last_seq": "e4f5g6h..." } ``` Pass `last_seq` as the `since` parameter on the next request to paginate forward. *** ## Optimistic locking with ETag The versioning system powers optimistic locking. When you read a file, the response includes an `ETag` header with the current git blob SHA. Pass this value in the `If-Match` header on writes to detect concurrent modifications. ```bash theme={null} # 1. Read the file and capture the ETag ETAG=$(curl -sI 'http://localhost:3333/api/kiwi/file?path=concepts/auth.md' \ | grep -i etag | awk '{print $2}' | tr -d '\r"') # 2. Write with If-Match to prevent conflicts curl -X PUT 'http://localhost:3333/api/kiwi/file?path=concepts/auth.md' \ -H "Content-Type: text/markdown" \ -H "X-Actor: agent:updater" \ -H "If-Match: $ETAG" \ -d '# Authentication (updated) Revised content.' ``` If another write occurred between your read and write, you receive a `409 Conflict` response: ```json theme={null} {"error": "conflict: file was modified since your last read"} ``` # Views (Bases) Source: https://docs.kiwifs.com/api/views List, save, delete, and execute saved DQL views stored in .kiwi/views. Base path: `http://localhost:3333/api/kiwi`. ## List views ```http theme={null} GET /views ``` ```json theme={null} { "views": [{ "name": "open-tasks", "query": "...", "layout": "table" }] } ``` ## Get or save a view ```http theme={null} GET /views/:name ``` Returns the full `View` object (`name`, `query`, `layout`, optional `columns`, `filters`, `sort`, `group_by`). ```http theme={null} PUT /views/:name Content-Type: application/json ``` Body is a `View` JSON object. The server forces `name` to match the URL parameter. ## Delete a view ```http theme={null} DELETE /views/:name ``` Returns `{ "status": "deleted" }`. ## Execute a view ```http theme={null} GET /views/:name/execute?limit=50&offset=0 ``` Runs the view's DQL query through the Dataview executor, applies optional column formulas, and returns the query result payload (rows plus metadata from the executor). Returns **`503`** if the Dataview executor is unavailable. ## MCP parity See `kiwi_views_list`, `kiwi_views_get`, `kiwi_views_save`, `kiwi_views_delete`, and `kiwi_views_execute` in [MCP](/concepts/mcp). # Webhooks Source: https://docs.kiwifs.com/api/webhooks Register, list, and delete webhooks for real-time change notifications. Webhooks must be enabled in configuration. See [Webhooks concept](/concepts/webhooks) for setup and signature verification. ## Register a webhook ```http theme={null} POST /api/kiwi/webhooks ``` HTTPS endpoint to receive POST notifications. Glob pattern to filter which file changes trigger this webhook. Defaults to all files. ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/webhooks' \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com/hook", "path_glob": "concepts/**"}' ``` Unique webhook identifier. Registered endpoint URL. Path filter pattern. HMAC signing secret (only returned at creation). ISO 8601 creation timestamp. Whether the webhook is active. Returns `503` if webhooks are not enabled in configuration. ## List webhooks ```http theme={null} GET /api/kiwi/webhooks ``` ```bash theme={null} curl 'http://localhost:3333/api/kiwi/webhooks' ``` Returns an array of webhooks. The `secret` field is omitted from list responses. ## Delete a webhook ```http theme={null} DELETE /api/kiwi/webhooks/:id ``` ```bash theme={null} curl -X DELETE 'http://localhost:3333/api/kiwi/webhooks/wh_abc123' ``` Returns `204 No Content` on success. # Workflows Source: https://docs.kiwifs.com/api/workflows CRUD workflow definitions and advance page state with validation. Workflow JSON files live in `.kiwi/workflows/`. Pages reference them with frontmatter keys `workflow` and `state`. Base path: `http://localhost:3333/api/kiwi`. ## List and read ```http theme={null} GET /workflows ``` ```json theme={null} { "workflows": [{ "name": "triage", "states": [...], "transitions": [...] }] } ``` ```http theme={null} GET /workflows/:name ``` Returns a single workflow definition. ## Save definition ```http theme={null} PUT /workflows/:name Content-Type: application/json ``` Body is the full workflow JSON. The server sets `name` from the URL segment and validates structure. ## Delete a workflow ```http theme={null} DELETE /workflows/:name ``` Removes a workflow definition. Pages referencing this workflow keep their current `state` but can no longer be advanced. ## Assign a page to a workflow ```http theme={null} POST /workflow/assign Content-Type: application/json ``` ```json theme={null} { "path": "tasks/t-42.md", "workflow": "triage", "state": "open", "actor": "agent:bot" } ``` Sets the `workflow` and `state` frontmatter fields on the page. Use this to onboard a page into a workflow for the first time. ## Advance a page ```http theme={null} POST /workflow/advance Content-Type: application/json ``` ```json theme={null} { "path": "tasks/t-42.md", "target_state": "done", "actor": "agent:bot" } ``` * Requires the page to have **`workflow`** and **`state`** in frontmatter. * Validates `(state → target_state)` against the workflow's `transitions`. * Writes the page with updated `state` via the normal pipeline. Returns **`409`** when the transition is not allowed. ## Reorder items on the board ```http theme={null} POST /workflow/reorder Content-Type: application/json ``` ```json theme={null} { "workflow": "triage", "state": "in_progress", "paths": ["tasks/t-42.md", "tasks/t-15.md", "tasks/t-99.md"] } ``` Sets the display order of pages within a workflow state column. The `paths` array defines the new order top-to-bottom. ## Kanban board ```http theme={null} GET /workflow/board/:workflow ``` Returns `{ "workflow": { ... }, "board": { "": [ { "path", "title", ... } ] } }` grouping markdown pages that declare the given workflow name. ## MCP parity `kiwi_workflow_list`, `kiwi_workflow_get`, `kiwi_workflow_save`, `kiwi_workflow_advance`, `kiwi_workflow_board` — see [MCP](/concepts/mcp). # CLI commands Source: https://docs.kiwifs.com/cli/commands KiwiFS command-line reference. All commands use the `kiwifs` binary. Run `kiwifs --help` to list available commands, or `kiwifs --help` for details on a specific command. ## Core commands Start the KiwiFS HTTP server and serve your knowledge base. ```bash theme={null} kiwifs serve [flags] ``` | Flag | Short | Default | Description | | ------------------ | ----- | ------------- | -------------------------------------------------- | | `--root` | `-r` | `./knowledge` | Knowledge root directory | | `--port` | `-p` | `3333` | HTTP port | | `--host` | | `0.0.0.0` | Bind address | | `--search` | | `sqlite` | Search engine: `sqlite` or `grep` | | `--versioning` | | `git` | Versioning strategy: `git`, `cow`, or `none` | | `--auth` | | `none` | Auth type: `none`, `apikey`, `perspace`, or `oidc` | | `--api-key` | | | API key (required if `auth=apikey`) | | `--oidc-issuer` | | | OIDC issuer URL | | `--oidc-client-id` | | | OIDC client ID | | `--async-commit` | | `true` | Enable async batched commits | | `--batch-window` | | `200` | Async commit batch window in milliseconds | | `--batch-max-size` | | `50` | Max paths per batch | | `--no-watch` | | `false` | Disable fsnotify watcher | **Protocol flags** | Flag | Default | Description | | --------------- | ------- | ----------------------------------------------------------- | | `--nfs` | `false` | Enable userspace NFSv3 server | | `--nfs-port` | `2049` | NFS port | | `--nfs-allow` | | CIDRs allowed to mount NFS | | `--s3` | `false` | Enable S3-compatible API | | `--s3-port` | `3334` | S3 port | | `--webdav` | `false` | Enable WebDAV server | | `--webdav-port` | `3335` | WebDAV port | | `--space` | | Register additional space (repeatable, format: `name=path`) | ```bash Basic theme={null} kiwifs serve --root ./my-docs --port 8080 ``` ```bash With auth theme={null} kiwifs serve --auth apikey --api-key my-secret-key ``` ```bash With NFS and S3 theme={null} kiwifs serve --nfs --nfs-allow 10.0.0.0/8 --s3 ``` ```bash Multi-space theme={null} kiwifs serve --space team=./team-docs --space personal=./my-notes ``` Create a new knowledge directory with an optional template. ```bash theme={null} kiwifs init [flags] ``` | Flag | Short | Default | Description | | ------------ | ----- | ------------- | ------------------------------------------------------------------------- | | `--root` | `-r` | `./knowledge` | Directory to initialize | | `--template` | | `knowledge` | Template: `knowledge`, `wiki`, `runbook`, `research`, `tasks`, or `blank` | ```bash Default template theme={null} kiwifs init ``` ```bash Wiki template in a custom directory theme={null} kiwifs init --root ./wiki --template wiki ``` ```bash Blank starting point theme={null} kiwifs init --root ./docs --template blank ``` Templates provide a starter directory structure with sample files. Use `blank` if you want an empty knowledge base. Start a Model Context Protocol server over stdio transport. Use this to connect KiwiFS to AI agents and LLM tools. ```bash theme={null} kiwifs mcp [flags] ``` | Flag | Default | Description | | ----------- | --------- | --------------------------------------------------- | | `--root` | | Knowledge root (in-process mode) | | `--remote` | | KiwiFS server URL (proxy mode) | | `--api-key` | | API key for remote server | | `--space` | `default` | Space to scope to | | `--http` | `false` | Enable Streamable HTTP transport (instead of stdio) | | `--port` | `8181` | HTTP port (only with `--http`) | Run KiwiFS directly within the MCP server process. Best for local development. ```bash theme={null} kiwifs mcp --root ./knowledge ``` Connect to a running KiwiFS server. Best for shared or remote setups. ```bash theme={null} kiwifs mcp --remote https://kiwi.example.com --api-key my-key ``` Serve MCP over Streamable HTTP instead of stdio. ```bash theme={null} kiwifs mcp --root ./knowledge --http --port 8181 ``` Mount a remote KiwiFS server as a local filesystem using FUSE. ```bash theme={null} kiwifs mount [flags] ``` | Flag | Default | Description | | -------------- | --------- | ----------------------------------------------------- | | `--remote` | | Remote KiwiFS server URL (required) | | `--api-key` | | API key (or `KIWIFS_API_KEY` env) | | `--bearer` | | Bearer token (or `KIWIFS_BEARER` env) | | `--basic-user` | | HTTP Basic auth username | | `--basic-pass` | | HTTP Basic auth password (or `KIWIFS_BASIC_PASS` env) | | `--space` | `default` | Space name | ```bash theme={null} kiwifs mount ~/mnt/kiwi --remote https://kiwi.example.com --api-key my-key ``` FUSE must be installed on your system. On macOS, install [macFUSE](https://osxfuse.github.io/). On Linux, install `fuse3`. ## Data commands Import data from databases, files, or third-party services into your knowledge base. ```bash theme={null} kiwifs import [flags] ``` | Flag | Short | Default | Description | | ------------- | ----- | ------------- | --------------------------------- | | `--from` | | | Source type (see list below) | | `--root` | `-r` | `./knowledge` | Knowledge root | | `--dsn` | | | Database connection string | | `--table` | | | Table or collection name | | `--query` | | | Custom SQL query | | `--file` | | | Path to data file | | `--prefix` | | | Path prefix in KiwiFS | | `--columns` | | | Comma-separated fields to include | | `--id-column` | | | Column to use as filename | | `--limit` | | | Max records to import | | `--dry-run` | | `false` | Preview without writing | **Supported sources** | Category | Sources | | --------- | ------------------------------------------------------------------------------------------- | | Databases | `postgres`, `mysql`, `sqlite`, `mongodb`, `firestore`, `dynamodb`, `redis`, `elasticsearch` | | Files | `csv`, `json`, `jsonl`, `yaml`, `excel` | | Services | `notion`, `airtable`, `gsheets`, `obsidian`, `confluence` | ```bash Import from PostgreSQL theme={null} kiwifs import --from postgres --dsn "postgres://user:pass@localhost/mydb" \ --table articles --id-column slug --prefix articles/ ``` ```bash Import from CSV theme={null} kiwifs import --from csv --file data.csv --id-column name --prefix people/ ``` ```bash Dry run from Notion theme={null} kiwifs import --from notion --dry-run ``` Use `--dry-run` first to preview which files will be created before writing anything. Export structured data (JSONL, CSV, Parquet) or render markdown into publication formats (PDF, HTML, slides, static site). ```bash theme={null} kiwifs export [flags] ``` **Data formats** | Flag | Short | Default | Description | | ---------------------- | ----- | ------------- | ----------------------------------------- | | `--root` | `-r` | `./knowledge` | Knowledge root | | `--format` | | `jsonl` | Data format: `jsonl`, `csv`, or `parquet` | | `--output` | `-o` | stdout | Output file | | `--path` | | | Scope to subdirectory | | `--columns` | | | Comma-separated frontmatter fields (CSV) | | `--include-content` | | `false` | Include full markdown body | | `--include-links` | | `false` | Include outgoing/incoming links | | `--include-embeddings` | | `false` | Include vector embeddings | | `--limit` | | `0` | Max files (0 = unlimited) | **Document formats** — use `--format pdf`, `html`, `slides`, or `site` with `--path` pointing at a file or directory: | Flag | Default | Description | | ------------------ | ------- | ---------------------------------------------------------------- | | `--theme` | | Theme: `paper`, `modern`, `minimal`, `dark`, `presentation` | | `--self-contained` | `false` | Embed assets in HTML/PDF output | | `--bibliography` | | Path to `.bib` or `.json` bibliography | | `--csl` | | Citation style: `apa`, `ieee`, `chicago`, `vancouver`, `harvard` | | `--crossref` | `false` | Enable figure/table/equation cross-references | | `--pdf-engine` | auto | PDF engine: `typst` or `xelatex` | | `--slide-format` | `html` | Slide output: `html`, `pdf`, or `pptx` | | `--site-name` | | Static site title (MkDocs) | | `--site-url` | | Canonical site URL | | `--repo-url` | | Edit-on-GitHub link for generated site | ```bash Export as JSONL theme={null} kiwifs export --format jsonl -o knowledge.jsonl ``` ```bash Export as Parquet theme={null} kiwifs export --format parquet --include-content -o knowledge.parquet ``` ```bash Render PDF theme={null} kiwifs export --format pdf --path docs/report.md --output report.pdf --theme paper ``` ```bash Render slide deck theme={null} kiwifs export --format slides --path talks/intro.md --output slides.html ``` See [Data export](/export/overview) and [Document export](/export/documents) for REST and MCP equivalents. Run a Dataview Query Language (DQL) query against your knowledge base. ```bash theme={null} kiwifs query "" [flags] ``` | Flag | Short | Default | Description | | -------- | ----- | ------------- | -------------- | | `--root` | `-r` | `./knowledge` | Knowledge root | ```bash theme={null} kiwifs query "TABLE title, status FROM 'concepts' WHERE status = 'draft'" ``` Run aggregation queries against frontmatter fields. ```bash theme={null} kiwifs aggregate [flags] ``` | Flag | Short | Default | Description | | --------- | ----- | ------- | -------------------------------------------------------------------------------------------- | | `--root` | `-r` | | Knowledge root | | `--group` | | | Frontmatter field to group by | | `--calc` | | | Aggregation functions: `count`, `avg`, `sum`, `min`, `max` (with field, e.g. `avg:priority`) | ```bash theme={null} kiwifs aggregate --root ./knowledge --group status --calc count --calc avg:priority ``` ## Maintenance commands Display a dashboard summarizing knowledge health, coverage, and engagement metrics (page views and failed searches). ```bash theme={null} kiwifs analytics [flags] ``` | Flag | Short | Default | Description | | ------------------- | ----- | ------------- | ---------------------------------- | | `--root` | `-r` | `./knowledge` | Knowledge root | | `--scope` | | | Path prefix to limit results | | `--stale-threshold` | | `30` | Days before a page counts as stale | | `--format` | | `text` | Output format: `text` or `json` | ```bash Full dashboard theme={null} kiwifs analytics --root ./knowledge ``` ```bash Scope to a folder theme={null} kiwifs analytics --scope students/ --stale-threshold 14 ``` ```bash JSON for automation theme={null} kiwifs analytics --format json ``` Check your knowledge base for issues such as broken links, missing frontmatter, or invalid YAML. ```bash theme={null} kiwifs lint [flags] ``` | Flag | Short | Default | Description | | -------- | ----- | ------- | -------------- | | `--root` | `-r` | | Knowledge root | ```bash theme={null} kiwifs lint --root ./knowledge ``` Scan for stale pages, orphans, broken links, duplicate titles, and other hygiene issues. Exits with code **1** when error-severity issues are found. ```bash theme={null} kiwifs janitor [flags] ``` | Flag | Short | Default | Description | | -------------- | ----- | ------------- | -------------------------------------- | | `--root` | `-r` | `./knowledge` | Knowledge root | | `--stale-days` | | `90` | Days before a page is considered stale | | `--json` | | `false` | Emit JSON instead of a human summary | ```bash theme={null} kiwifs janitor --root ./knowledge --stale-days 60 --json ``` Rebuild the SQLite FTS5 index from markdown files on disk. When vector search is configured, also rebuilds the vector index unless `--fts-only` is set. ```bash theme={null} kiwifs reindex [flags] ``` | Flag | Short | Default | Description | | ------------ | ----- | ------------- | -------------------------------------------------------------- | | `--root` | `-r` | `./knowledge` | Knowledge root | | `--vector` | | `false` | Force vector index rebuild (requires `[search.vector]` config) | | `--fts-only` | | `false` | Skip vector index even when configured | ```bash Rebuild FTS index theme={null} kiwifs reindex --root ./knowledge ``` ```bash Rebuild FTS and vector indexes theme={null} kiwifs reindex --root ./knowledge --vector ``` Reindexing locks writes briefly. Run this during low-traffic periods on production servers. Create and regenerate computed view files (`kiwi-view: true` in frontmatter). ```bash theme={null} kiwifs view [subcommand] [flags] ``` | Subcommand | Purpose | | ---------------- | -------------------------------------------- | | `refresh ` | Regenerate a view file from its `kiwi-query` | | `list` | List all computed views | | `create` | Create a new view file and regenerate it | | Flag | Short | Default | Description | | --------- | ----- | ------------- | ------------------------------------------------- | | `--root` | `-r` | `./knowledge` | Knowledge root | | `--query` | | | DQL query (`create` only, required) | | `--name` | | | View title and filename (`create` only, required) | | `--path` | | | Directory for the new file (`create` only) | ```bash List views theme={null} kiwifs view list --root ./knowledge ``` ```bash Refresh one view theme={null} kiwifs view refresh dashboards/active-students.md ``` ```bash Create a new view theme={null} kiwifs view create --query 'TABLE name, status FROM "students/" WHERE status = "active"' --name "Active Students" --path reports/ ``` ## Rules, tokens, benchmarks, clipping, and memory View or export the markdown rules file that steers agents across harnesses. ```bash theme={null} kiwifs rules kiwifs rules edit kiwifs rules export --format cursor kiwifs rules sync --format cursor --output .cursor/rules/kiwi.md ``` | Subcommand | Purpose | | ----------- | ------------------------------------------------------------ | | *(default)* | Print `rules.md` (local) or fetch from `--remote`. | | `edit` | Open the file in `$EDITOR`. | | `export` | Emit Cursor, Claude, AGENTS, or OpenClaw formatted snippets. | | `sync` | Export and write to `--output`. | Remote operations accept `--remote` and `--api-key` like other commands. Create scoped keys (read/write/admin) for automation or per-space access. ```bash theme={null} kiwifs token create --root ./knowledge --space docs --scope read --actor ci-reader kiwifs token list --root ./knowledge kiwifs token revoke kiwi_ro_abc1deadbeef ``` Tokens append to `.kiwi/config.toml`; the plaintext key is shown once on create. Runs in a temporary directory with an in-process stack to benchmark writes, bulk writes, reads, and search. ```bash theme={null} kiwifs bench --files 200 --bulk 120 --search-files 5000 --search-queries 100 ``` Outputs a markdown table suitable for issues or release notes. ```bash theme={null} kiwifs clip https://example.com/article --root ./knowledge --folder clips/ --tags research,web ``` Flags: `--title`, `--tags`, `--folder`, `--actor` (defaults to `clipper`). ```bash theme={null} kiwifs memory report --root ./knowledge --json kiwifs memory report --episodes-prefix agent-runs/ --json ``` Summarizes episodic files vs merged pages for merge pipelines (no LLM consolidation). | Flag | Default | Description | | ------------------- | ------------- | ---------------------------------------- | | `--root` | `./knowledge` | Knowledge root | | `--json` | `false` | Emit JSON instead of text | | `--episodes-prefix` | from config | Override `[memory] episodes_path_prefix` | ## KiwiFS Cloud commands Log in via OAuth device flow. Credentials are stored in `~/.kiwifs/credentials.json`. ```bash theme={null} kiwifs login kiwifs login --host https://app.kiwifs.com ``` | Flag | Default | Description | | ------------- | ------------------------ | ------------------------------------ | | `--host` | `https://app.kiwifs.com` | KiwiFS Cloud host | | `--client-id` | auto | WorkOS client ID (optional override) | ```bash theme={null} kiwifs logout ``` Removes `~/.kiwifs/credentials.json`. ```bash theme={null} kiwifs whoami ``` Prints the email and display name for the stored Cloud session. Generate MCP server configuration for connecting an agent to a KiwiFS Cloud workspace. ```bash theme={null} kiwifs connect [flags] ``` | Flag | Default | Description | | ----------- | ------------------------ | ------------------------------------------------------------------------------------------ | | `--key` | `$KIWI_API_KEY` | API key (optional after `kiwifs login`) | | `--write` | | Write config to a client: `cursor`, `claude-code`, `windsurf`, `claude-desktop`, or `auto` | | `--host` | `https://api.kiwifs.com` | KiwiFS Cloud API host | | `--project` | `false` | Write project-level config instead of global | ```bash Print config for any MCP client theme={null} kiwifs connect my-workspace --key kiwi_sk_abc123 ``` ```bash Auto-write to Cursor theme={null} kiwifs connect my-workspace --write cursor --project ``` Download and install the latest `kiwifs` binary from GitHub Releases. ```bash theme={null} kiwifs update ``` Replaces the currently running binary in place. ## Backup and restore Push your knowledge base to a git remote for backup. ```bash theme={null} kiwifs backup [flags] ``` | Flag | Short | Default | Description | | ---------------------- | ----- | ------- | ------------------------------------------------------ | | `--root` | `-r` | | Knowledge root | | `--remote` | | | Git remote URL (overrides config) | | `--branch` | | | Branch to push | | `--rebase-before-push` | | `true` | Fetch and rebase onto the backup branch before pushing | ```bash theme={null} kiwifs backup --root ./knowledge --remote git@github.com:org/knowledge.git ``` By default, `kiwifs backup` fetches and rebases onto the backup branch before pushing. See [Backup sync](/deploy/backup-sync) for configuration, opt-outs, and conflict handling. Clone a knowledge base from a git remote. ```bash theme={null} kiwifs restore [flags] ``` | Flag | Default | Description | | ---------- | ------- | -------------------------- | | `--from` | | Git remote URL (required) | | `--to` | | Local directory (required) | | `--branch` | | Branch to check out | ```bash theme={null} kiwifs restore --from git@github.com:org/knowledge.git --to ./knowledge ``` # Agent Interface Source: https://docs.kiwifs.com/concepts/agent-interface How AI agents read and write markdown in KiwiFS. KiwiFS gives agents four ways to access markdown, from the most native (filesystem) to the most structured (MCP). ## Filesystem access When your agent has a real filesystem mount (NFS, FUSE, or local): ```bash theme={null} cat /kiwi/concepts/authentication.md grep -r "timeout" /kiwi/ ls /kiwi/reports/ echo "# New finding" > /kiwi/reports/finding-042.md ``` Agents already know these commands from training data. No SDK, no driver, no custom protocol. ## REST API When a filesystem mount isn't available: ```bash theme={null} # Read a file curl localhost:3333/api/kiwi/file?path=concepts/auth.md # Write a file (with attribution) curl -X PUT 'localhost:3333/api/kiwi/file?path=reports/finding.md' \ -H "X-Actor: agent:exec_abc" \ -H "X-Provenance: run:run-249" \ -d "# Finding..." # Search curl 'localhost:3333/api/kiwi/search?q=timeout' ``` ## MCP (Model Context Protocol) The most structured interface, designed for Claude, Cursor, and other AI tools: ```bash theme={null} kiwifs mcp --root ~/knowledge # in-process, no server needed kiwifs mcp --remote http://host:3333 # proxy to a running server ``` **60+ MCP tools** cover files, search, DQL, imports, analytics, drafts, saved views (Bases), canvas, workflows, claims, graph analytics, linting, clipping, and more. See the grouped reference in [MCP](/concepts/mcp). See [MCP](/concepts/mcp) for transport options, HTTP security notes, and resource URIs. ## Access protocols Every protocol flows through the same storage layer. Every write — regardless of how it enters — gets a git commit, a search index update, and an SSE broadcast. flowchart TD subgraph Protocols REST\["REST API :3333"] NFS\["NFS :2049"] S3\["S3 :3334"] WebDAV\["WebDAV :3335"] FUSE\["FUSE mount"] MCP\["MCP stdio/HTTP"] end REST & NFS & S3 & WebDAV & FUSE & MCP --> Storage subgraph KiwiFS\["KiwiFS Server"] Storage\["Storage Layer"] --> Git\["Git Commit"] Git --> Index\["Search Index"] Index --> SSE\["SSE Broadcast"] end | Protocol | Use case | Example | | ------------ | ----------------------------------- | ------------------------------------------------- | | **REST API** | Web frontend, scripts | `curl localhost:3333/api/kiwi/file?path=index.md` | | **MCP** | AI agents (Claude, Cursor) | `kiwifs mcp --root ~/knowledge` | | **NFS** | Docker, Kubernetes (native mount) | `docker run --mount type=nfs,...` | | **S3** | Backup, data pipelines | `aws s3 sync s3://knowledge/ /backup/` | | **WebDAV** | Windows mapped drives, legacy tools | Map Network Drive in Explorer | | **FUSE** | Developer workstations | `kiwifs mount --remote http://server:3333 ~/kiwi` | Setup guides for NFS, S3, WebDAV, and FUSE: [Alternate protocols](/guides/alternate-protocols). ## Provenance tracking Know which agent run produced which content: ```bash theme={null} curl -X PUT localhost:3333/api/kiwi/file?path=report.md \ -H "X-Actor: agent:exec_abc" \ -H "X-Provenance: run:run-249" \ -d "# Run 249 Report..." ``` KiwiFS injects `derived-from` into the frontmatter automatically. Query later: "show me every page produced by run-249." # Agent playbook Source: https://docs.kiwifs.com/concepts/agent-playbook SCHEMA.md, playbook.md, rules.md, and kiwi_context — how agents onboard to a knowledge base. Every `kiwifs init` template ships agent-facing documents beside your markdown pages. Together they tell agents **where** to write, **what** frontmatter to use, and **which** MCP tools to call. ## Core files | File | Purpose | | ------------------- | -------------------------------------------------------------- | | `SCHEMA.md` | Directory layout, frontmatter field tables, naming conventions | | `.kiwi/playbook.md` | Step-by-step MCP sequences for each operation | | `.kiwi/rules.md` | Harness-specific behavior (Cursor, Claude, etc.) | | `index.md` | Auto-maintained table of contents | | `log.md` | Optional append-only chronological record | The **`knowledge`** template also includes `episodes/` for episodic memory. See [Episodic memory](/concepts/episodic-memory). ## Load context in one call ``` kiwi_context ``` Returns `schema`, `playbook`, and `index` in one response. ```bash theme={null} curl -s 'http://localhost:3333/api/kiwi/context' ``` ```json theme={null} { "playbook": "# Playbook\n...", "schema": "# Schema\n...", "index": "# Index\n..." } ``` MCP also exposes read-only resource **`kiwi://schema`** for the schema document alone. ## Playbook operations ``` kiwi_context kiwi_search("authentication") kiwi_write("pages/auth.md", content) kiwi_read("index.md") kiwi_write("index.md", updated_index) kiwi_append("log.md", "- added auth page") ``` ``` kiwi_search("payment timeout") kiwi_read("pages/payments.md") ``` ``` kiwi_append("episodes/2026-05-21.md", observation) ``` ``` kiwi_memory_report kiwi_read("episodes/2026-05-21.md") kiwi_write("pages/topic.md", merged_content) ``` ``` kiwi_analytics kiwi_health_check("pages/auth.md") kiwi_lint ``` ## Rules for your harness Export `.kiwi/rules.md` in a format your agent tool understands: ```bash Print rules theme={null} kiwifs rules ``` ```bash Export for Cursor theme={null} kiwifs rules export --format cursor ``` ```bash Sync to project config theme={null} kiwifs rules sync --format cursor --output .cursor/rules/kiwi.md ``` REST: `GET /api/kiwi/rules`, `PUT /api/kiwi/rules` — see [Utilities API](/api/utilities#rules-file). ## Provenance on writes ```bash theme={null} curl -X PUT 'http://localhost:3333/api/kiwi/file?path=episodes/run-249.md' \ -H 'X-Actor: agent:researcher' \ -H 'X-Provenance: run:run-249' \ -d '# Session notes...' ``` KiwiFS injects `derived-from` into frontmatter automatically from these headers. ## Related documentation Full tool reference. Episodes, consolidation, and merge reports. JSON Schema validation on writes. Practical workflow recipes. # Audit log Source: https://docs.kiwifs.com/concepts/audit-log Track every API request with structured audit entries for compliance and debugging. When audit logging is enabled, KiwiFS records every API request as a structured entry. Use audit logs for compliance, debugging, and understanding how agents and users interact with your knowledge base. ## Enabling audit logging ```toml .kiwi/config.toml theme={null} [audit] enabled = true ``` Without this setting, the audit endpoint returns `501 Not Implemented`. ## Querying the audit log ```http theme={null} GET /api/kiwi/audit?since=2026-05-01T00:00:00Z&limit=100 ``` ISO 8601 timestamp. Defaults to the last 24 hours. Maximum entries to return (max 10,000). ```json theme={null} [ { "ts": "2026-05-02T14:30:00Z", "method": "PUT", "path": "/api/kiwi/file?path=concepts/auth.md", "space": "default", "actor": "agent:docs-writer", "token_hash": "a1b2c3...", "ip": "10.0.0.5", "status": 200, "duration_ms": 42 } ] ``` ## Entry fields | Field | Description | | ------------- | ------------------------------------------------------ | | `ts` | Timestamp of the request | | `method` | HTTP method (`GET`, `PUT`, `POST`, `DELETE`) | | `path` | Full request path including query parameters | | `space` | Space name (from URL segment or `X-Kiwi-Space` header) | | `actor` | Value of `X-Actor` header, if present | | `token_hash` | Hash of the Bearer token used for authentication | | `ip` | Client IP address | | `status` | HTTP response status code | | `duration_ms` | Request processing time in milliseconds | ## Use cases Audit logs provide a tamper-evident record of who accessed or modified knowledge. Filter by `actor` or `token_hash` to trace activity by a specific agent or user. When an agent writes unexpected content, query the audit log by time range and actor to reconstruct the sequence of API calls. Use `duration_ms` to identify slow requests. Combine with [Prometheus metrics](/api/overview#prometheus-metrics) for dashboards. The `X-Space` header is also recorded when present, allowing you to trace cross-space operations in multi-space deployments. ## Related documentation Enable audit logging in config.toml. Feature matrix — audit requires explicit opt-in. REST endpoint details. # Bases and saved views Source: https://docs.kiwifs.com/concepts/bases-views Named DQL queries with columns, filters, and layout — table, list, calendar, or kanban over your frontmatter. **Bases** in the web UI are powered by **saved views**: JSON definitions stored under `.kiwi/views/.json`. Each view stores a **DQL query** plus presentation metadata (columns, filters, sort, `group_by`, layout). ## View definition | Field | Purpose | | ---------- | --------------------------------------------------------------------------------------- | | `name` | Stable identifier (matches the filename without `.json`). | | `query` | DQL string executed by the Dataview engine (same language as [DQL](/concepts/dql)). | | `layout` | One of `table`, `list`, `calendar`, `kanban`. | | `columns` | Optional column specs with `property`, `label`, optional `formula`, optional `summary`. | | `filters` | Structured filters (`field`, `operator`, `value`). | | `sort` | Sort keys (`field`, `order`: `asc` or `desc`). | | `group_by` | Optional grouping field for board-style layouts. | Computed views in markdown (frontmatter `kiwi-view: true`) are separate from saved views; use `POST /api/kiwi/view/refresh` or the `kiwi_view_refresh` MCP tool for those. ## Related documentation * [Views REST API](/api/views) * [MCP](/concepts/mcp) — `kiwi_views_list`, `kiwi_views_get`, `kiwi_views_save`, `kiwi_views_delete`, `kiwi_views_execute` # Canvas files Source: https://docs.kiwifs.com/concepts/canvas JSON Canvas files for spatial diagrams — nodes, edges, interactive Flow rendering, and graph-assisted layout. Canvas files are JSON documents with a **`.canvas.json`** suffix. They store `nodes` and `edges` for infinite-canvas layouts in the web UI and for agents via API or MCP. ## File format Each canvas is JSON with top-level `nodes` and `edges` arrays. Node and edge objects carry `id`, `type`, optional coordinates (`x`, `y`), and a `data` map for extensibility. Common node types include file references, text labels, link arrows, and grouped regions. ## Web UI Click **Canvas** in the toolbar to open the canvas hub. You can create, rename, and switch between canvases stored under `canvases/`. The interactive **Flow** renderer lets you drag nodes, pan and zoom, and edit the diagram visually. Changes persist to the underlying `.canvas.json` file. ## Graph-assisted layout `POST /api/kiwi/canvas/generate` builds a new canvas from the **wiki-link graph** using layout modes such as `hierarchical`, `radial`, `force`, or `circular`. Restrict to a folder prefix so only part of the graph is laid out. For canvases that already exist, `POST /api/kiwi/canvas/auto-layout?path=...` repositions nodes using Graphviz engines (`dot`, `neato`, `fdp`, `circo`). ## Incremental edits Agents can apply batched operations with `PATCH /api/kiwi/canvas` instead of rewriting the full JSON document. See the [Canvas REST API](/api/canvas) for supported `op` values. ## Related documentation List, read, write, patch, query, and auto-layout. Excalidraw diagrams in markdown files. `kiwi_canvas_list`, `kiwi_canvas_read`, `kiwi_canvas_write`. Toolbar views including Canvas and Whiteboard. # Task claims Source: https://docs.kiwifs.com/concepts/claims Short-lived leases so multiple agents do not edit the same page at the same time. Claims are **cooperative locks** on file paths. An agent that wants exclusive access **claims** a path with a lease duration; others receive a conflict response until the lease expires or the holder **releases** the claim. ## Requirements * The server must be configured with a claim store. If claims are disabled, APIs return `503` / "claims not enabled". * `POST /claim` and `DELETE /claim` require the **`X-Actor`** header so the server knows who holds the lease. ## Lease duration Request body may include `lease_duration` as a Go duration string (for example `30m`, `1h`). The server clamps leases between **1 minute** and **24 hours** (default **30 minutes** if omitted). ## Related documentation * [Claims REST API](/api/claims) * [MCP](/concepts/mcp) — `kiwi_claim`, `kiwi_release`, `kiwi_claims_list` # DQL Source: https://docs.kiwifs.com/concepts/dql A SQL-like query language for frontmatter metadata across your knowledge base. DQL (DataView Query Language) lets you query frontmatter metadata across your entire knowledge base. It is inspired by Obsidian Dataview but runs server-side against the SQLite metadata index. ## Query modes DQL supports four output modes. ```sql TABLE theme={null} TABLE title, status, priority FROM "concepts" WHERE status = "draft" SORT priority DESC ``` ```sql LIST theme={null} LIST FROM "reports" ``` ```sql COUNT theme={null} COUNT FROM "entities" ``` ```sql DISTINCT theme={null} DISTINCT author FROM "documents" ``` * **TABLE** returns tabular results with the fields you specify. * **LIST** returns a flat list of matching page paths. * **COUNT** returns the number of matching pages. * **DISTINCT** returns unique values for a single field. ## Clauses | Clause | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `FROM "path/"` | Scope the query to a directory. | | `WHERE` | Filter with boolean logic (`AND`, `OR`, `NOT`), comparison operators (`=`, `!=`, `>`, `<`, `>=`, `<=`), `CONTAINS`, and `MATCHES` (regex). | | `SORT field ASC\|DESC` | Sort results by a field. | | `GROUP BY field` | Group results by a field. | | `FLATTEN field` | Expand array fields into multiple rows. | | `LIMIT n` | Cap the number of results. | ## Implicit fields Every page exposes these fields without any frontmatter: | Field | Description | | ---------- | ---------------------------------------------- | | `_path` | File path relative to the knowledge base root. | | `_updated` | Last modified timestamp. | | `_size` | File size in bytes. | | `_words` | Word count of the page body. | ```sql theme={null} TABLE _path, _words FROM "concepts" WHERE _words > 1000 SORT _words DESC ``` ## Expressions DQL supports arithmetic, function calls, string interpolation, comparisons, and boolean logic inside `WHERE` and computed field definitions. ```sql theme={null} TABLE title, $.priority * 10 + len($.tags) AS score FROM "concepts" SORT score DESC ``` ## Built-in functions DQL ships with 27 functions: `now()`, `date()`, `duration()`, `days_since()`, `format()` `lower()`, `upper()`, `trim()`, `split()`, `join()`, `contains()`, `regextest()` `round()`, `floor()`, `ceil()`, `abs()`, `min()`, `max()` `len()`, `sum()`, `avg()`, `first()`, `last()`, `sort()`, `reverse()`, `unique()` `meta()` — access implicit fields programmatically. ## CLI usage Run a DQL query from the command line: ```bash theme={null} kiwifs query "TABLE title, status FROM 'concepts' WHERE status = 'draft'" ``` ## REST API Run a DQL query over HTTP: ``` GET /api/kiwi/query?q=TABLE title, status FROM "concepts" WHERE status = "draft" ``` ## Aggregation Use the `aggregate` command to group and summarize metadata: ```bash theme={null} kiwifs aggregate --group status --calc count,avg:priority ``` Supported aggregation functions: `count`, `avg`, `sum`, `min`, `max`. You can group by any frontmatter field. ## Computed views A markdown file with `kiwi-view: true` in its frontmatter becomes a computed view. KiwiFS auto-refreshes the file body from a DQL query embedded in the frontmatter. ```yaml theme={null} --- kiwi-view: true query: "TABLE title, status FROM 'concepts' WHERE status = 'draft' SORT _updated DESC" --- ``` The body of this file is overwritten with live query results on every index update. Think of it as a saved query that always shows current data. ## Computed frontmatter fields You can define virtual fields in `.kiwi/config.toml` that are evaluated at index time. These fields are queryable like any other frontmatter field. ```toml theme={null} [dataview] computed_fields.age_days = "days_since(updated)" computed_fields.is_long = "len(body) > 5000" computed_fields.priority_score = "priority * 10 + len(tags)" ``` Computed fields are recalculated on every reindex. They do not modify your source files. # Draft spaces Source: https://docs.kiwifs.com/concepts/draft-spaces Git-backed staging branches so agents can prepare changes before merging to the live knowledge base. Draft spaces are **isolated worktrees** tied to your knowledge repository. Each draft has its own branch: agents read and write files inside the draft without touching the main tree until you **merge** or **discard**. ## When to use drafts * Long-running agent sessions that should not publish partial work. * Reviewing a batch of edits as a single diff before one merge commit. * Parallel agents each with their own draft (subject to server limits). ## Lifecycle 1. **Create** a draft — server allocates an ID and a git branch. 2. **Read / write / delete / tree** inside the draft using the draft API or MCP tools (`kiwi_draft_*`). 3. **Diff** against the main branch to see what would change. 4. **Merge** to apply all draft changes to the live knowledge base (with normal indexing and versioning), or **discard** to delete the draft branch. If the server returns `501` / "drafts not enabled", this process was started without a draft manager. Use the same KiwiFS version and configuration template as the HTTP server that advertises draft APIs. ## Related documentation * [Drafts REST API](/api/drafts) * [MCP](/concepts/mcp) — `kiwi_draft_create`, `kiwi_draft_list`, `kiwi_draft_read`, `kiwi_draft_write`, `kiwi_draft_diff`, `kiwi_draft_merge`, `kiwi_draft_discard` # Episodic memory Source: https://docs.kiwifs.com/concepts/episodic-memory Separate raw agent sessions from durable semantic pages using frontmatter conventions and merge reports. KiwiFS provides a **data model** for agent memory systems: episodic files accumulate raw observations, semantic pages hold durable knowledge, and a **merge report** shows which episodes are not yet referenced by any downstream page. KiwiFS does not run an LLM. Consolidation is your job; KiwiFS gives you conventions and tooling. ## Two layers Per-run or per-session raw notes. High volume, append-friendly, lives under `episodes/`. Durable curated knowledge on entity and concept pages. Built from episodes after consolidation. ## Provenance vs consolidation | Frontmatter | Set by | Meaning | | -------------- | ----------------------------------- | -------------------------------------------------------- | | `derived-from` | KiwiFS (from `X-Provenance` header) | Which run or job **produced** this file | | `merged-from` | You (or your consolidation job) | Which episodes were **folded into** this downstream page | `derived-from` records authorship. `merged-from` records consumption. The memory report uses only `merged-from` to determine coverage. ## Classify pages with `memory_kind` | Value | Role | | --------------- | ------------------------------------- | | `episodic` | Raw run or session material | | `semantic` | Durable curated knowledge | | `consolidation` | Staging area before merge to semantic | | `working` | Scratch, high churn | `memory_kind: semantic` or `consolidation` prevents a file from being treated as episodic even when it lives under the episodes path prefix. ## Path convention By default, markdown under **`episodes/`** is treated as episodic when `memory_kind` is absent or set to `episodic`. ```toml .kiwi/config.toml theme={null} [memory] episodes_path_prefix = "episodes/" ``` ## Episodic identity and `merged-from` Prefer **`episode_id`** in episodic frontmatter. If absent, KiwiFS falls back to `id`. ```yaml frontmatter example theme={null} merged-from: - type: episode id: run-7f3a date: "2026-04-27T12:00:00Z" note: "merged into concepts/auth from nightly job" ``` An episode is **covered** when any page references it in `merged-from` — by `id` or by compatible `type` (`run`, `session`, `trace`, `event`, `ingest`). ## Operator workflow Write under `episodes/` with `X-Provenance` and optional `episode_id` in frontmatter. Read unmerged episodes, consolidate with your LLM or rules, then update `pages/` with `merged-from`. Re-run the memory report until `unmerged` is empty or acceptable. ## Check coverage ```bash theme={null} kiwifs memory report --root ./knowledge kiwifs memory report --root ./knowledge --json kiwifs memory report --episodes-prefix raw/ ``` ```bash theme={null} curl -s 'http://localhost:3333/api/kiwi/memory/report' curl -s 'http://localhost:3333/api/kiwi/memory/report?episodes_prefix=raw/&limit=10&offset=0' ``` Call **`kiwi_memory_report`** with optional `episodes_prefix`, `limit`, and `offset`. ## Related documentation `[memory]` section in config.toml. Response fields for the memory report endpoint. # Files as truth Source: https://docs.kiwifs.com/concepts/files-as-truth Why KiwiFS uses plain markdown files as the source of truth. In KiwiFS, every page is a **plain markdown file** with optional YAML frontmatter. There is no database, no proprietary format, no schema migration. The filesystem *is* the database. ## Why files? * **Universal** — every language, tool, and agent can read and write files. * **Durable** — files outlast databases, SaaS tools, and proprietary formats. * **Diffable** — git gives you full history, blame, and merge for free. * **Inspectable** — `cat`, `grep`, `find`, `tree` work out of the box. * **Portable** — copy the directory to move your knowledge anywhere. ## Frontmatter as structured data Each file can include YAML frontmatter for structured metadata: ```markdown theme={null} --- title: Authentication status: verified tags: [security, oauth] author: agent:docs-writer updated: 2026-04-25 --- # Authentication OAuth2 + JWT based authentication system... ``` KiwiFS indexes this frontmatter into SQLite, making it queryable via [DQL](/concepts/dql) and the [metadata API](/api/metadata). ## The `.kiwi/` directory KiwiFS stores its configuration and internal data in a `.kiwi/` directory at the root of your knowledge base: ## Git versioning When `versioning = "git"` (the default), every write — via REST, MCP, NFS, or filesystem — creates a git commit. The commit author is set from the `X-Actor` header or MCP `actor` parameter. ```bash theme={null} $ git log --oneline e4f5g6h agent:docs-writer update concepts/auth.md a1b2c3d agent:importer create concepts/auth.md ``` This gives you: * **Full history** for every page * **Blame** to trace which agent wrote which line * **Diff** between any two versions * **Rollback** to any previous state ## Implications Because files are the source of truth: 1. **Backup** is `git push` or `rsync`. 2. **Import** adds files; **export** reads them. 3. **Migration** is copying a directory. 4. **Multiple tools** can operate on the same knowledge base (KiwiFS, git, editors, scripts). 5. **No vendor lock-in** — if you stop using KiwiFS, your markdown files remain. # MCP (Model Context Protocol) Source: https://docs.kiwifs.com/concepts/mcp Connect Claude, Cursor, and other AI tools to KiwiFS — stdio, HTTP, and 60+ tools. KiwiFS ships a built-in MCP server so agents can work with the same operations as the REST API and web UI. ## Start the MCP server ```bash theme={null} # In-process (reads files directly, no HTTP server required) kiwifs mcp --root ~/knowledge # Proxy mode (forwards to a running KiwiFS server) kiwifs mcp --remote http://host:3333 --api-key kiwi_sk_... # Streamable HTTP transport (remote agents, LAN tools) kiwifs mcp --root ~/knowledge --http --port 8181 ``` Use `--space ` when the knowledge root hosts multiple configured spaces. Remote HTTP MCP should stay behind TLS and reuse the same API keys you trust for REST. ## Configure Claude Desktop / Cursor ```json theme={null} { "mcpServers": { "kiwifs": { "command": "kiwifs", "args": ["mcp", "--root", "/path/to/knowledge"] } } } ``` ## Tool inventory The server currently registers **60+** tools (exact count may change release to release). They are grouped below; every name is invoked as listed. | Tool | Summary | | ---------------------- | ------------------------------------------------------------------------------------------------ | | `kiwi_read` | Read markdown with optional wiki-link resolution, metadata-only mode, and `if_not_etag` caching. | | `kiwi_write` | Create/update a page (git-versioned) with optional `actor` and `provenance`. | | `kiwi_delete` | Delete a path (history preserved when git versioning is on). | | `kiwi_rename` | Move/rename with optional wiki-link rewrites across the tree. | | `kiwi_bulk_write` | Atomic multi-file commit. | | `kiwi_append` | Append without read-modify-write races. | | `kiwi_tree` | Directory listing with depth and optional permalinks. | | `kiwi_search` | BM25 full-text search with pagination and `path_prefix`. | | `kiwi_search_semantic` | Vector similarity search (requires embeddings). | | `kiwi_changes` | Incremental change list since a checkpoint. | | `kiwi_versions` | Git history entries for a path. | | `kiwi_view_refresh` | Regenerate a computed `kiwi-view` markdown file. | | Tool | Summary | | ------------------------ | ----------------------------------------------------------------- | | `kiwi_query_meta` | Frontmatter filters with AND/OR groups, sort, pagination. | | `kiwi_query` | DQL queries (`TABLE`, `LIST`, `COUNT`, `DISTINCT`, …). | | `kiwi_aggregate` | `GROUP BY` style aggregates over metadata. | | `kiwi_import` | Pull from databases, CSV/JSON, Notion, Airtable, etc. | | `kiwi_export` | Export JSONL/CSV with optional embeddings. | | `kiwi_analytics` | Knowledge-base health metrics. | | `kiwi_health_check` | Single-page diagnostics. | | `kiwi_memory_report` | Episodic memory coverage / merge hints. | | `kiwi_velocity` | Change hotspots, cold pages, bursts. | | `kiwi_eval` | Search quality benchmark harness. | | `kiwi_context` | Schema, playbook, index, and rules in one call — good first tool. | | `kiwi_backlinks` | Incoming wiki links for a page. | | `kiwi_suggestions` | Semantically similar pages excluding existing links. | | `kiwi_embeddings` | Raw chunk embeddings for a page. | | `kiwi_graph_analytics` | PageRank-ish graph summary. | | `kiwi_graph_centrality` | PageRank and betweenness tables. | | `kiwi_graph_communities` | Louvain communities. | | `kiwi_graph_path` | Shortest path between two pages. | | `kiwi_graph_walk` | Local neighborhood / siblings around a page. | | `kiwi_eligible` | Ranked todo-style tasks inferred via metadata heuristics. | | Tool | Summary | | -------------------------------------- | ----------------------------------- | | `kiwi_draft_create` | Open a new git-backed draft branch. | | `kiwi_draft_list` | List active drafts. | | `kiwi_draft_read` / `kiwi_draft_write` | Work inside a draft by `draft_id`. | | `kiwi_draft_diff` | Unified diff vs main. | | `kiwi_draft_merge` | Merge draft into live knowledge. | | `kiwi_draft_discard` | Drop the draft branch. | | Tool | Summary | | -------------------- | -------------------------------------------------- | | `kiwi_views_list` | List `.kiwi/views/*.json` definitions. | | `kiwi_views_get` | Fetch a single view definition. | | `kiwi_views_save` | Persist columns, filters, sort, layout, and query. | | `kiwi_views_delete` | Remove a saved view. | | `kiwi_views_execute` | Run the DQL backing a view and return rows. | | Tool | Summary | | ---------------------------------------- | ------------------------------- | | `kiwi_canvas_list` | Discover `*.canvas.json` files. | | `kiwi_canvas_read` / `kiwi_canvas_write` | JSON canvas payloads. | Canvas **generate**, **patch**, **query**, and **auto-layout** are REST-only — see [Canvas API](/api/canvas). | Tool | Summary | | ----------------------------------------------------------------- | ----------------------------------------------------------------- | | `kiwi_workflow_list` / `kiwi_workflow_get` / `kiwi_workflow_save` | Manage `.kiwi/workflows/*.json`. | | `kiwi_workflow_advance` | Validate and apply a frontmatter state transition. | | `kiwi_workflow_board` | Kanban-style board grouped by `state`. | | `kiwi_claim` / `kiwi_release` | Cooperative leases on paths (requires actor headers in practice). | | `kiwi_claims_list` | Inspect active claims. | | Tool | Summary | | ---------------------- | -------------------------------------------------------------- | | `kiwi_peek` | Lightweight summary (title, snippet, links, headings). | | `kiwi_section` | Extract a single heading section by name or index. | | `kiwi_timeline` | Recent git-backed activity with pagination metadata. | | `kiwi_feed` | Serialized activity feed (JSON or Atom depending on mode). | | `kiwi_clip` | Fetch a URL and persist clipped markdown. | | `kiwi_ingest` | Convert supported binary documents to markdown via MarkItDown. | | `kiwi_export_document` | Render markdown to PDF, HTML, slides, or a static site. | | `kiwi_lint` | Structural markdown lint for a path or inline content. | | `kiwi_eval` | Benchmark full-text and semantic search quality. | ## Resources Read-only MCP resources: | URI pattern | Description | | -------------------- | --------------------------------------------------------- | | `kiwi://schema` | `SCHEMA.md` (or generated schema) for the knowledge root. | | `kiwi://file/{path}` | Markdown bytes for a relative path. | | `kiwi://tree/{path}` | Directory listing rooted at `{path}`. | ## Further reading * [Agent interface](/concepts/agent-interface) — how MCP fits next to REST and mounts. * [Agent playbook](/concepts/agent-playbook) — `kiwi_context` and playbook operations. * [Episodic memory](/concepts/episodic-memory) — `kiwi_memory_report`. * REST counterparts: [Drafts](/api/drafts), [Views](/api/views), [Canvas](/api/canvas), [Workflows](/api/workflows), [Claims](/api/claims), [Publish](/api/publish), [Utilities](/api/utilities), [Import and export](/api/import-export#import-browse), [Document export](/export/documents). # Multi-space Source: https://docs.kiwifs.com/concepts/multi-space Run multiple isolated knowledge bases on one KiwiFS server with path or header routing. Multi-space mode lets one KiwiFS process host **multiple independent knowledge bases**. Each space has its own root directory, git repo, search index, and `.kiwi/` state. ## When to use multi-space * Separate team wikis on one host (engineering vs product). * Per-tenant isolation in self-hosted deployments. * A default space plus dynamically provisioned workspaces. ## Configure spaces ```toml .kiwi/config.toml theme={null} [[spaces]] name = "engineering" root = "/data/eng-wiki" [[spaces]] name = "product" root = "/data/product-wiki" ``` ```bash theme={null} kiwifs serve \ --space engineering=/data/eng-wiki \ --space product=/data/product-wiki ``` The first registered space is the **default** — requests without a space identifier route there. ## Request routing Resolution order: | Priority | Method | Example | | -------- | --------------------- | ----------------------------------------------- | | 1 | `X-Kiwi-Space` header | Set by a reverse proxy (subdomain → space name) | | 2 | URL path prefix | `/api/kiwi/{space}/tree` | | 3 | Default space | First registered space | ```bash Path-based routing theme={null} curl 'http://localhost:3333/api/kiwi/engineering/tree?path=' ``` ```bash Header-based routing theme={null} curl 'http://localhost:3333/api/kiwi/tree?path=' \ -H 'X-Kiwi-Space: engineering' ``` If `X-Kiwi-Space` is set but the space does not exist, the request fails immediately — no silent fallback to the default. ## Space admin API | Method | Path | Purpose | | -------- | ------------------- | ----------------------------- | | `GET` | `/api/spaces` | List all spaces with metadata | | `GET` | `/api/spaces/:name` | Single space summary | | `POST` | `/api/spaces` | Create `{ "name", "root" }` | | `DELETE` | `/api/spaces/:name` | Remove a dynamic space | Dynamic spaces persist to `.kiwi/spaces.json` under the first space's root and restore on restart. See [Spaces API](/api/spaces) for curl examples. ## Per-space auth With `auth.type = "perspace"`, API keys scope to a single space: ```toml .kiwi/config.toml theme={null} [[auth.api_keys]] key = "kiwi_sk_eng_..." space = "engineering" actor = "eng-team" ``` ## MCP Use `--space ` when the knowledge root hosts multiple configured spaces: ```bash theme={null} kiwifs mcp --root /data --space engineering ``` ## Related documentation `[[spaces]]` config syntax. REST CRUD for space management. # Optional features Source: https://docs.kiwifs.com/concepts/optional-features Which subsystems need configuration and what HTTP status codes mean when they are off. Several KiwiFS features are **optional**. Disabled subsystems return a clear HTTP error instead of failing silently. ## Feature matrix | Feature | How to enable | Error when disabled | | ------------------ | -------------------------------- | ------------------------------ | | Draft spaces | Git versioning (not `none`) | `501` | | Claims | Claim store at bootstrap | `503` | | Audit log | `[audit] enabled = true` | `501` | | Vector search | `[search.vector] enabled = true` | `503` | | Webhooks | `[webhooks] enabled = true` | Empty CRUD | | Schema enforcement | `[schema] enforce = true` | Writes pass without validation | | Graph analytics | Link index available | `503` | | Import connections | Store init success | `[]` from list endpoint | ## Enabling subsystems Drafts require git-backed branches. Ensure versioning is not `none`: ```toml .kiwi/config.toml theme={null} [versioning] strategy = "git" ``` See [Draft spaces](/concepts/draft-spaces). Task claims prevent two agents from editing the same path concurrently. The claim store is wired at bootstrap when the server detects the feature is needed. See [Claims](/concepts/claims). ```toml .kiwi/config.toml theme={null} [audit] enabled = true ``` ```bash theme={null} curl 'http://localhost:3333/api/kiwi/audit?since=2026-05-01T00:00:00Z&limit=100' ``` See [Utilities API](/api/utilities#audit-log). ```toml .kiwi/config.toml theme={null} [search.vector] enabled = true [search.vector.embedder] provider = "openai" model = "text-embedding-3-small" api_key = "${OPENAI_API_KEY}" [search.vector.store] provider = "sqlite-vec" ``` See [Search](/concepts/search) and [Configuration](/configuration#vector-search). ```toml .kiwi/config.toml theme={null} [schema] enforce = true ``` When enabled, writes with invalid frontmatter are rejected. Define types under `.kiwi/schemas/`. See [Schemas](/concepts/schemas). If you see `501` or `503` on an endpoint, check the table above. These are not bugs — they mean the subsystem was not configured for this server instance. ## Related documentation Full config.toml reference. Auth and error conventions. # Publishing and sharing Source: https://docs.kiwifs.com/concepts/publishing Make pages publicly accessible via publish flags, share links, or space visibility. KiwiFS provides three ways to make content accessible without authentication: **per-page publishing**, **share links**, and **space-level visibility**. ## Per-page publishing Set `published: true` in a page's frontmatter to make it accessible at `/p/{path}` without authentication. ```yaml theme={null} --- title: "Getting Started" published: true published_at: "2026-05-02T14:30:00Z" --- ``` Published pages are served as rendered HTML at their public permalink: ``` http://localhost:3333/p/concepts/auth.md ``` ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/publish' \ -H 'Content-Type: application/json' \ -d '{"path":"concepts/auth.md"}' ``` Use `kiwi_write` to set `published: true` in frontmatter directly. Toggle the publish switch in the page header menu. ### Bulk operations Publish or unpublish multiple pages at once: ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/publish/bulk' \ -H 'Content-Type: application/json' \ -d '{"paths":["concepts/auth.md","concepts/billing.md"]}' ``` ### Listing published pages ```bash theme={null} curl 'http://localhost:3333/api/kiwi/publish/list' ``` See [Publish API](/api/publish) for full endpoint reference. ## Share links Create time-limited, optionally password-protected links to individual pages. Share links work independently from publishing — a page does not need to be published to be shared. ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/share' \ -H 'Content-Type: application/json' \ -d '{"path": "concepts/auth.md", "expiresIn": "168h", "password": "optional-secret"}' ``` Recipients open the returned token URL without API authentication: ``` GET /api/kiwi/public/:token ``` Share links have their own view counter. Revoke a link with `DELETE /api/kiwi/share/:id` using the share `id` (not the long token). ## Space visibility Control access at the space level with the `visibility` setting in `.kiwi/config.toml`: ```toml theme={null} [space] visibility = "private" # "private", "unlisted", or "public" ``` | Visibility | Behavior | | ---------- | ------------------------------------------------------------------------------------ | | `private` | All access requires authentication (default) | | `unlisted` | Accessible to anyone with the URL, not listed publicly | | `public` | Pages with `visibility: public` in frontmatter are browseable without authentication | ### Public visibility browsing When space visibility allows it, pages that declare `visibility: public` in their frontmatter are accessible via dedicated endpoints: ```bash theme={null} curl 'http://localhost:3333/api/kiwi/public/file?path=concepts/open.md' curl 'http://localhost:3333/api/kiwi/public/tree?path=' ``` Non-public pages return `404` to avoid leaking existence. ### Changing visibility at runtime ```bash theme={null} curl -X PUT 'http://localhost:3333/api/kiwi/space/visibility' \ -H 'Content-Type: application/json' \ -d '{"visibility": "public"}' ``` ## How the models interact | Model | Scope | Auth needed to read? | Persisted where? | | ---------------- | ---------------- | ------------------------------ | ------------------------------------------------------------------ | | Published pages | Single page | No — served at `/p/{path}` | `published: true` in frontmatter | | Share links | Single page | No — token in URL | Server-side share store | | Space visibility | All public pages | No — via `/public/*` endpoints | `[space] visibility` in config + per-page `visibility` frontmatter | Use **publishing** for content you want permanently public (docs, guides). Use **share links** for temporary or restricted sharing (reviews, previews). Use **space visibility** to open an entire knowledge base for browsing. ## Related documentation REST endpoints for publish, unpublish, and status. Share links, comments, and real-time events. Space visibility settings. Feature matrix and error codes. # Real-time events Source: https://docs.kiwifs.com/concepts/real-time-events Subscribe to write, delete, and bulk events over Server-Sent Events (SSE). KiwiFS broadcasts knowledge-base changes over **Server-Sent Events (SSE)**. Use `GET /api/kiwi/events` for live updates in the web UI or external integrations. There is no WebSocket endpoint. SSE is the only real-time transport. ## Event stream ```bash theme={null} curl -N 'http://localhost:3333/api/kiwi/events' ``` ```text SSE output theme={null} event: write data: {"path":"concepts/auth.md","actor":"agent:docs-writer","timestamp":"2026-04-25T14:30:00Z"} event: delete data: {"path":"concepts/old-page.md","actor":"agent:cleanup","timestamp":"2026-04-25T14:31:00Z"} event: bulk data: {"paths":["a.md","b.md"],"actor":"agent:importer","timestamp":"2026-04-25T14:32:00Z"} event: import data: {"imported":42,"source":"postgres","timestamp":"2026-04-25T14:33:00Z"} ``` ## Event types | Event | When it fires | | -------- | ----------------------------- | | `write` | A file was created or updated | | `delete` | A file was deleted | | `bulk` | A bulk write completed | | `import` | An import job finished | ## Client guidance The SSE connection stays open indefinitely. Implement reconnection logic with backoff. Send the `Last-Event-ID` header to resume where you left off. * Use `curl -N` (no buffer) or a native SSE client library. * Authenticate the same way as other `/api/kiwi` routes when auth is enabled. * Events fire after the full pipeline: storage → git → index → broadcast. ## Pipeline parity Writes through **any** protocol (NFS, S3, WebDAV, FUSE, REST, MCP) emit the same event types. ## Related documentation Protocol overview. Outbound HTTP notifications (complementary to SSE). # Schema Validation Source: https://docs.kiwifs.com/concepts/schemas Enforce structure on your markdown with JSON Schema validation on writes. Schema validation lets you enforce structure on pages by type. When enabled, every write validates the page's frontmatter against a JSON Schema before committing. This prevents agents and users from creating malformed pages. ## How it works flowchart LR Write\["PUT /file"] --> Check Check -- No --> Commit\["Git Commit"] Check -- Yes --> Validate\["Validate against\nschemas/type.json"] Validate -- Pass --> Commit Validate -- Fail --> Reject\["400 Bad Request"] 1. A page is written with a `type` field in its frontmatter. 2. KiwiFS looks up `.kiwi/schemas/{type}.json`. 3. If the schema exists and enforcement is on, the frontmatter is validated. 4. Invalid pages are rejected before they reach git. ## Enabling enforcement ```toml theme={null} [schema] enforce = true ``` When `enforce = false` (default), schemas are stored but not checked on writes. ## Creating schemas Schemas live in `.kiwi/schemas/` as standard JSON Schema files. The filename (minus `.json`) matches the frontmatter `type` field. ```bash theme={null} curl -X PUT 'http://localhost:3333/api/kiwi/schemas/runbook' \ -H "Content-Type: application/json" \ -d '{ "type": "object", "required": ["title", "status", "owner"], "properties": { "title": {"type": "string"}, "status": {"type": "string", "enum": ["draft", "reviewed", "verified"]}, "owner": {"type": "string"}, "review-by": {"type": "string", "format": "date"} } }' ``` Now any page with `type: runbook` in its frontmatter must have `title`, `status`, and `owner`: ```markdown theme={null} --- type: runbook title: Deploy Checklist status: draft owner: team-platform --- # Deploy Checklist ... ``` ## Listing schemas ```bash theme={null} curl 'http://localhost:3333/api/kiwi/schemas' ``` ```json theme={null} ["runbook", "concept", "episode"] ``` ## Reading a schema ```bash theme={null} curl 'http://localhost:3333/api/kiwi/schemas/runbook' ``` Returns the raw JSON Schema document. ## Validation errors When a write fails validation, you get a `400` response with details: ```json theme={null} { "error": "schema validation failed", "type": "runbook", "violations": [ "missing required property: owner", "status must be one of: draft, reviewed, verified" ] } ``` Use schemas to standardize page types across your knowledge base. For example, define schemas for `runbook`, `decision`, `episode`, and `concept` to ensure agents always create well-formed pages. # Search Source: https://docs.kiwifs.com/concepts/search Three tiers of search: grep, SQLite FTS5, and vector embeddings. KiwiFS provides three tiers of search, from zero-dependency grep to semantic vector similarity. You can start simple and scale up without changing your data or workflow. ## Tier 1: grep Zero dependencies. Exact string matching. Works everywhere. ```bash theme={null} kiwifs serve --search grep ``` Best for small knowledge bases or environments where SQLite isn't available. ## Tier 2: SQLite FTS5 (default) BM25-ranked full-text search powered by SQLite FTS5. Supports boolean operators, phrase matching, and path prefix filtering. ```bash theme={null} kiwifs serve --search sqlite ``` This is the default. KiwiFS builds and maintains a FTS5 index automatically. Searches are fast even with thousands of pages. ### Query syntax | Syntax | Example | Behavior | | ----------- | --------------------- | ---------------------------------- | | Keywords | `payment timeout` | Match pages containing both terms | | Boolean AND | `payment AND timeout` | Explicit AND | | Boolean OR | `payment OR billing` | Match pages containing either term | | Boolean NOT | `payment NOT refund` | Exclude pages containing a term | | Phrase | `"payment timeout"` | Match the exact phrase | ### Trust-ranked search KiwiFS also offers trust-ranked search at `/api/kiwi/search/verified`. Pages with `status: verified` or `source-of-truth: true` in their frontmatter are boosted in results. ## Tier 3: Vector search Semantic similarity search using pluggable embedder providers and vector stores. Enable it in `.kiwi/config.toml`: ```toml theme={null} [search.vector] enabled = true [search.vector.embedder] provider = "openai" model = "text-embedding-3-small" api_key = "${OPENAI_API_KEY}" [search.vector.store] provider = "sqlite-vec" ``` ### Supported embedders | Provider | Notes | | ----------- | -------------------------------------------------- | | OpenAI | `text-embedding-3-small`, `text-embedding-3-large` | | Ollama | Local models, no API key needed | | Cohere | `embed-english-v3.0`, multilingual variants | | Vertex AI | Google Cloud embeddings | | Bedrock | AWS embeddings | | Custom HTTP | Any endpoint returning a vector array | ### Supported vector stores | Store | Notes | | ---------- | ------------------------------- | | sqlite-vec | Embedded, zero config (default) | | Qdrant | Self-hosted or cloud | | pgvector | PostgreSQL extension | | Pinecone | Cloud-hosted | | Weaviate | Self-hosted or cloud | | Milvus | Self-hosted or cloud | ## API endpoints | Endpoint | Method | Description | | --------------------------- | --------- | -------------------------- | | `/api/kiwi/search` | GET | Full-text search | | `/api/kiwi/search/verified` | GET | Trust-ranked search | | `/api/kiwi/search/semantic` | GET, POST | Vector similarity search | | `/api/kiwi/meta` | GET | Frontmatter metadata query | ## MCP tools | Tool | Description | | ---------------------- | -------------------------------- | | `kiwi_search` | Full-text search with pagination | | `kiwi_search_semantic` | Vector similarity search | | `kiwi_query_meta` | Frontmatter metadata query | | `kiwi_query` | DQL queries over metadata | See the [Search API reference](/api/search) for detailed endpoint documentation. # Versioning Source: https://docs.kiwifs.com/concepts/versioning Git-backed versioning strategies: full git, copy-on-write, or none. KiwiFS versions every write so you never lose data. Choose the strategy that fits your use case. ## Strategies Configure via CLI flag or `.kiwi/config.toml`: ```bash theme={null} kiwifs serve --versioning git ``` ```toml theme={null} [versioning] strategy = "git" ``` ### git (default) Every write creates a real git commit. You get full `git log`, `git blame`, `git diff`, and `git revert` for free. The `X-Actor` header becomes the commit author. ```bash theme={null} $ git log --oneline knowledge/ e4f5g6h agent:docs-writer update concepts/auth.md a1b2c3d agent:importer create concepts/auth.md ``` Best for: production, audit trails, multi-agent environments. ### cow (copy-on-write) Lighter-weight versioning that stores snapshots without a real git repo. Useful when you want rollback capability but don't need the full git workflow. ```toml theme={null} [versioning] strategy = "cow" max_versions = 100 # max snapshots per file ``` Best for: high-write environments, embedded use cases. ### none No versioning at all. Writes overwrite files in place. Use this for ephemeral or scratch knowledge bases. Best for: development, temporary data, CI pipelines. ## Async batching To reduce git overhead under high write volume, KiwiFS batches commits: ```toml theme={null} [versioning] async_commit = true batch_window_ms = 200 # collect writes for 200ms batch_max_size = 50 # then commit up to 50 paths in one commit ``` Multiple writes within the batch window are combined into a single git commit. This dramatically improves throughput while preserving atomicity per batch. ## Optimistic locking The versioning system powers optimistic concurrency control via ETags. When you read a file, the response includes an `ETag` header containing the git blob SHA. ```bash theme={null} # Read and capture ETag curl -i 'http://localhost:3333/api/kiwi/file?path=concepts/auth.md' # ETag: "a1b2c3d4..." # Write with If-Match curl -X PUT 'http://localhost:3333/api/kiwi/file?path=concepts/auth.md' \ -H "If-Match: a1b2c3d4..." \ -d "updated content" ``` If the file was modified between your read and write, you get a `409 Conflict` response. ## API endpoints | Endpoint | Method | Description | | -------------------- | ------ | --------------------------------------------- | | `/api/kiwi/versions` | GET | Version history for a file | | `/api/kiwi/version` | GET | Read a specific version | | `/api/kiwi/diff` | GET | Unified diff between two versions | | `/api/kiwi/blame` | GET | Per-line git blame | | `/api/kiwi/changes` | GET | List recent changes across the knowledge base | See the [Versioning API reference](/api/versioning) for detailed endpoint documentation. # Webhooks Source: https://docs.kiwifs.com/concepts/webhooks Push real-time notifications to external services when pages change. Webhooks let you push change events to external services whenever pages are created, updated, or deleted. Each webhook receives a signed HTTP POST with details about the change. ## Enabling webhooks Add the `[webhooks]` section to your `.kiwi/config.toml`: ```toml theme={null} [webhooks] enabled = true max_workers = 4 max_retries = 3 ``` | Key | Default | Description | | ------------- | ------- | ------------------------------- | | `enabled` | `false` | Enable the webhook subsystem. | | `max_workers` | `4` | Concurrent dispatch goroutines. | | `max_retries` | `3` | Retries on delivery failure. | ## Creating a webhook Register a webhook URL with an optional glob pattern to filter which paths trigger it. ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/webhooks' \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com/hook", "path_glob": "concepts/**"}' ``` ```json theme={null} { "id": "wh_abc123", "url": "https://example.com/hook", "path_glob": "concepts/**", "secret": "whsec_k9x2m...", "created_at": "2026-05-04T12:00:00Z", "enabled": true } ``` The `secret` is only returned once at creation time. Store it securely — you'll need it to verify webhook signatures. ## Path glob patterns | Pattern | Matches | | -------------- | ------------------------------------- | | `**` | All files (default) | | `concepts/**` | All files under `concepts/` | | `reports/*.md` | Markdown files directly in `reports/` | Glob matching uses Go's `filepath.Match` with an added `**` recursive wildcard. ## Payload format Every webhook receives a JSON POST body: ```json theme={null} { "type": "write", "path": "concepts/auth.md", "actor": "agent:docs-writer", "timestamp": "2026-05-04T12:05:00Z" } ``` | Field | Description | | ----------- | -------------------------------------------------- | | `type` | Event type: `write`, `delete`, `bulk`, `import`. | | `path` | File path that changed. | | `actor` | The `X-Actor` header from the originating request. | | `timestamp` | ISO 8601 timestamp of the change. | ## Signature verification Every webhook POST includes three headers for verification: | Header | Description | | ------------------- | ------------------------------------ | | `webhook-id` | Unique message ID for deduplication. | | `webhook-timestamp` | Unix timestamp of the dispatch. | | `webhook-signature` | `v1,` HMAC-SHA256 signature. | The signature is computed over `{webhook-id}.{webhook-timestamp}.{body}` using the webhook secret as the HMAC key. ```python theme={null} import hmac, hashlib, base64 def verify(secret, msg_id, timestamp, body, signature): signed_content = f"{msg_id}.{timestamp}.{body}" expected = hmac.new( base64.b64decode(secret), signed_content.encode(), hashlib.sha256 ).digest() return hmac.compare_digest( base64.b64encode(expected).decode(), signature.removeprefix("v1,") ) ``` ## Managing webhooks ### List all webhooks ```bash theme={null} curl 'http://localhost:3333/api/kiwi/webhooks' ``` The `secret` field is omitted from list responses for security. ### Delete a webhook ```bash theme={null} curl -X DELETE 'http://localhost:3333/api/kiwi/webhooks/wh_abc123' ``` # Whiteboard Source: https://docs.kiwifs.com/concepts/whiteboard Excalidraw diagrams stored as markdown files — create, edit, and share freeform drawings in the web UI. Whiteboards are freeform diagrams stored as markdown files with an **`.excalidraw.md`** suffix. KiwiFS embeds Excalidraw scene data inside the file so drawings version with the rest of your knowledge base. ## File location New whiteboards are created under `whiteboards/` by default: ``` whiteboards/brainstorm.excalidraw.md whiteboards/architecture.excalidraw.md ``` Each file uses YAML frontmatter and a fenced JSON block that Excalidraw can parse. The drawing data lives in a ` ```json ` fence under `## Drawing`. ## Web UI Click **Whiteboard** in the toolbar. The hub lists all `.excalidraw.md` files in your knowledge base. Use **New whiteboard** to create a file under `whiteboards/`, or pick an existing board from the dropdown. Use Excalidraw tools to sketch, annotate, and connect shapes. Changes save automatically to the markdown file on disk. You can also open a whiteboard from the file tree like any other page. Excalidraw markdown renders inline in page view. Whiteboards are plain markdown files. Agents can read and write them with `kiwi_read` and `kiwi_write` like any other page — no separate API is required. ## Related documentation Toolbar views and file tree. Structured JSON canvas diagrams. How KiwiFS stores everything on disk. # Wiki links Source: https://docs.kiwifs.com/concepts/wiki-links Link pages with [[wiki-link]] syntax and explore the knowledge graph. KiwiFS supports `[[wiki-link]]` syntax in markdown files. Wiki links create navigable connections between pages and power the knowledge graph. ## Syntax | Syntax | Description | | ----------------------------- | ---------------------------------------------------------------------------- | | `[[page-name]]` | Link to a page by filename. KiwiFS resolves the target using fuzzy matching. | | `[[path/to/page]]` | Link to a page by its full path. | | `[[page-name\|Display text]]` | Link with custom display text. | ```markdown theme={null} See [[authentication]] for details on token validation. Related: [[concepts/rate-limiting|Rate limiting]] and [[concepts/caching|Caching]]. ``` ## Link resolution KiwiFS resolves wiki links using fuzzy matching. You do not need exact paths. `[[auth]]` resolves to `concepts/authentication.md` if that is the closest match. Resolve links programmatically: ``` POST /api/kiwi/resolve-links Content-Type: application/json {"links": ["auth", "rate-limiting"]} ``` ## Backlinks Every page shows a "Linked from" panel listing all pages that link to it. You can query backlinks via the API: ``` GET /api/kiwi/backlinks?path=concepts/auth.md ``` This returns an array of pages that contain a wiki link pointing to `concepts/auth.md`. ## Knowledge graph The web UI includes a full knowledge graph visualization built with Sigma.js and the ForceAtlas2 layout algorithm. Open it from the graph icon in the sidebar. Each node is a page. Each edge is a wiki link. The graph updates in real time as you create, edit, or delete pages. Fetch the raw graph data: ``` GET /api/kiwi/graph ``` The response contains all nodes (pages) and edges (links) in the knowledge base. ## Orphan detection Pages with no incoming links are flagged as orphans in the analytics dashboard. Orphans are often pages that were created but never referenced, or pages whose referring pages were deleted. Find orphans via the API or CLI: ```bash CLI theme={null} kiwifs analytics ``` ```bash API theme={null} GET /api/kiwi/analytics ``` ## Broken link detection Links pointing to non-existent pages are tracked and reported in analytics. Run `kiwifs lint` to check for broken links across the entire knowledge base: ```bash theme={null} kiwifs lint ``` Broken links do not prevent pages from rendering. They appear as plain text in the web UI and are flagged in the lint report. # Workflows Source: https://docs.kiwifs.com/concepts/workflows JSON state machines in .kiwi/workflows — pages carry workflow and state in frontmatter for Kanban-style boards. Workflows let you define **allowed transitions** between named states. Pages opt in via frontmatter: * `workflow: ` — which definition from `.kiwi/workflows/.json` applies. * `state: ` — current state. Definitions include `states` (each with `name`, optional `color`, optional `terminal`) and `transitions` (`from`, `to`, optional `required_role`). ## Advancing state `POST /api/kiwi/workflow/advance` validates that a transition exists from the page's current `state` to the requested `target_state`, then updates frontmatter and commits through the normal pipeline. ## Board view `GET /api/kiwi/workflow/board/:workflow` returns the workflow JSON plus pages grouped by `state` — useful for Kanban UIs. ## Related documentation * [Workflows REST API](/api/workflows) * [MCP](/concepts/mcp) — `kiwi_workflow_list`, `kiwi_workflow_get`, `kiwi_workflow_save`, `kiwi_workflow_advance`, `kiwi_workflow_board` # Configuration Source: https://docs.kiwifs.com/configuration Configure KiwiFS via .kiwi/config.toml — search, auth, versioning, vector embeddings, and more. KiwiFS is configured via `.kiwi/config.toml` inside your knowledge root directory. All settings are optional — KiwiFS runs with sensible defaults out of the box. Environment variables in the form `${VAR}` are expanded in all string values. ## Server ```toml theme={null} [server] host = "0.0.0.0" port = 3333 cors_origins = ["https://app.example.com"] public_url = "https://wiki.example.com" [server.cors] allowed_origins = ["https://app.example.com"] allowed_methods = ["GET", "POST", "PUT", "PATCH", "DELETE"] allow_credentials = true max_age = 86400 ``` The `public_url` is used for permalink generation. Override via `KIWI_PUBLIC_URL` env var. Use `cors_origins` for a simple allowlist, or `[server.cors]` for fine-grained CORS control (methods, credentials, preflight cache). ### Rate limiting ```toml theme={null} [server.rate_limit] requests_per_minute = 600 burst_size = 50 ``` When `requests_per_minute` is greater than zero, KiwiFS applies a token-bucket limiter keyed by Bearer token hash (or client IP when anonymous). Health, readiness, and metrics routes are exempt. ## Search ```toml theme={null} [search] engine = "sqlite" # "sqlite" (default) or "grep" async_index = true index_window_ms = 200 index_batch_max = 50 ``` ### Vector search Enable semantic search by configuring an embedder and a vector store. ```toml theme={null} [search.vector] enabled = true worker_count = 5 # default; lower for small local embedders [search.vector.embedder] provider = "openai" # openai | ollama | cohere | voyage | http | bedrock | vertex model = "text-embedding-3-small" api_key = "${OPENAI_API_KEY}" dimensions = 1536 # timeout = "120s" # optional for Ollama; default is 30s [search.vector.store] provider = "sqlite-vec" # sqlite-vec | qdrant | pgvector | pinecone | weaviate ``` ### Chunking Control how documents are split before embedding: ```toml theme={null} [search.vector.chunk] size = 512 overlap = 64 ``` | Field | Default | Description | | --------- | ------- | ---------------------------------------- | | `size` | `512` | Target tokens per chunk | | `overlap` | `64` | Token overlap between consecutive chunks | For local Ollama on a small CPU-only VPS, lower `worker_count` and raise the Ollama request timeout. See [Ollama vector search on a small VPS](/deploy/ollama-vector-search). | Provider | Required fields | | --------- | ------------------------------ | | `openai` | `api_key`, `model` | | `ollama` | `base_url`, `model` | | `cohere` | `api_key`, `model` | | `voyage` | `api_key`, `model` | | `http` | `url` (custom endpoint) | | `bedrock` | `region`, `model` | | `vertex` | `project`, `location`, `model` | | Provider | Notes | | ------------ | ------------------------------------------------------ | | `sqlite-vec` | Zero-config, embedded (default when vector is enabled) | | `qdrant` | Requires separate Qdrant server | | `pgvector` | Requires PostgreSQL with pgvector extension | | `pinecone` | Cloud-hosted | | `weaviate` | Self-hosted or cloud | ## Versioning ```toml theme={null} [versioning] strategy = "git" # "git" (default), "cow", or "none" async_commit = true batch_window_ms = 200 batch_max_size = 50 max_versions = 100 # only for "cow" strategy ``` * **git** — every write is a real git commit. Full history, blame, diff. * **cow** — copy-on-write snapshots without git. Lighter, limited history. * **none** — no versioning. Writes overwrite in place. ## Authentication ```toml theme={null} [auth] type = "none" # "none", "apikey", "perspace", or "oidc" api_key = "kiwi_sk_..." # for type = "apikey" # Per-space keys [[auth.api_keys]] key = "kiwi_sk_eng_..." space = "engineering" actor = "eng-team" # OIDC [auth.oidc] issuer = "https://accounts.google.com" client_id = "your-client-id" ``` Auth is hot-reloadable — send `SIGHUP` to the server process to reload auth configuration from disk without restarting. ## Assets ```toml theme={null} [assets] max_file_size = "10MB" allowed_types = ["image/png", "image/jpeg", "image/gif", "image/webp", "application/pdf"] ``` ## UI ```toml theme={null} [ui] theme_locked = false # when true, users cannot change the theme ``` ## Backup ```toml theme={null} [backup] remote = "git@github.com:org/knowledge.git" interval = "5m" branch = "main" rebase_before_push = true # default: true ``` KiwiFS fetches and rebases onto the backup branch before pushing by default. This avoids routine non-fast-forward failures when another client pushed to the same backup branch first. Override via env: `KIWI_BACKUP_REMOTE`, `KIWI_BACKUP_INTERVAL`, `KIWI_BACKUP_REBASE_BEFORE_PUSH`. See [Backup sync](/deploy/backup-sync) for the full workflow, opt-outs, and troubleshooting. ## Janitor The janitor periodically scans for stale pages, orphans, and broken links. ```toml theme={null} [janitor] interval = "1h" stale_days = 30 startup_scan = true ``` ## DataView ```toml theme={null} [dataview] max_scan_rows = 10000 query_timeout = "5s" max_auto_indexes = 20 computed_fields = true [dataview.custom_fields] age_days = "days_since(updated)" is_long = "len(body) > 5000" ``` ## Memory Episodic vs semantic conventions for agent memory. See [Episodic memory](/concepts/episodic-memory). ```toml theme={null} [memory] episodes_path_prefix = "episodes/" ``` ## Drafts ```toml theme={null} [drafts] enabled = true max_active = 10 auto_discard = "720h" ``` `auto_discard` is a duration string (for example `"720h"` for 30 days). Draft branches older than this threshold are discarded automatically. ## Workflow ```toml theme={null} [workflow] enforce_transitions = false transitions = ["open->in_progress", "in_progress->done", "in_progress->blocked", "blocked->in_progress"] ``` | Field | Default | Description | | --------------------- | ------- | ----------------------------------------------------------------------------------------------- | | `enforce_transitions` | `false` | When `true`, `POST /workflow/advance` rejects transitions not listed in the workflow definition | | `transitions` | `[]` | Global default transitions (workflow definitions can override per-workflow) | ## Audit ```toml theme={null} [audit] enabled = true ``` Requires `[audit] enabled = true` for `GET /api/kiwi/audit`. See [Optional features](/concepts/optional-features). ## Webhooks ```toml theme={null} [webhooks] enabled = true max_workers = 4 max_retries = 3 ``` See [Webhooks](/concepts/webhooks). Register static webhooks at startup with `[[webhook_entries]]`: ```toml theme={null} [[webhook_entries]] url = "https://hooks.example.com/kiwifs" events = ["file.created", "file.updated", "file.deleted"] secret = "${WEBHOOK_SECRET}" path_glob = "concepts/**" ``` ## Lint ```toml theme={null} [lint] auto_format = true reject_errors = true ``` When `auto_format` is true (default), KiwiFS normalizes markdown on write (table alignment, trailing whitespace). When `reject_errors` is true (default), writes that fail lint checks with error severity are rejected. ## Schema enforcement ```toml theme={null} [schema] enforce = false ``` When `true`, writes are validated against `.kiwi/schemas/`. See [Schemas](/concepts/schemas). ## Import (Airbyte) ```toml theme={null} [import] airbyte_api_key = "${AIRBYTE_API_KEY}" airbyte_workspace_id = "${AIRBYTE_WORKSPACE_ID}" docker_host = "unix:///var/run/docker.sock" prefer_airbyte = true image_prefix = "airbyte/" pull_policy = "if-not-present" ``` | Field | Default | Description | | ---------------- | ---------------- | --------------------------------------------------------- | | `docker_host` | `$DOCKER_HOST` | Docker socket for local Airbyte connectors | | `prefer_airbyte` | `true` | Route network sources through Airbyte when available | | `image_prefix` | `airbyte/` | Prefix for connector Docker images | | `pull_policy` | `if-not-present` | Image pull policy: `always`, `if-not-present`, or `never` | See [Airbyte import](/import/airbyte) and [Import connections](/import/connections). ## Tracing Query tracing is enabled by default and writes structured JSON records of what KiwiFS did during each request. ```toml theme={null} [tracing] enabled = true # default true; set false to suppress output = "stderr" # "stderr" (default) or "file" file = "/var/log/kiwifs-trace.jsonl" ``` ## Space visibility Control whether a space is accessible without authentication. ```toml theme={null} [space] visibility = "private" # "private" (default), "unlisted", or "public" ``` | Value | Behavior | | ---------- | ------------------------------------------------------ | | `private` | Requires authentication for all access | | `unlisted` | Accessible to anyone with the URL, not listed publicly | | `public` | Browseable and searchable without authentication | Visibility can also be changed at runtime via `PUT /api/kiwi/space/visibility`. ## Multi-space Run multiple independent knowledge bases from one server. ```toml theme={null} [[spaces]] name = "engineering" root = "/data/eng-wiki" visibility = "private" [[spaces]] name = "product" root = "/data/product-wiki" visibility = "unlisted" ``` Each space gets its own API under `/api/kiwi/{name}/...` or via the `X-Kiwi-Space` header. Administrators use `/api/spaces` to create and list spaces. You can also add spaces via CLI flags: `kiwifs serve --space eng=/data/eng --space product=/data/product`. See [Multi-space](/concepts/multi-space) and [Spaces API](/api/spaces). # Backup sync Source: https://docs.kiwifs.com/deploy/backup-sync Push your KiwiFS knowledge root to a git remote and avoid routine non-fast-forward backup failures. KiwiFS can back up a knowledge root by pushing its git repository to a remote. If another server, laptop, teammate, or git client pushes to the same backup branch first, a later backup push can fail with a non-fast-forward error. KiwiFS avoids the common clean case by fetching the backup branch and rebasing local backup commits before it pushes. ## Configure a backup remote Add a `[backup]` section to `.kiwi/config.toml` inside your knowledge root. ```toml theme={null} [backup] remote = "git@github.com:org/knowledge.git" interval = "5m" branch = "main" # Optional. Defaults to true. rebase_before_push = true ``` Start KiwiFS normally. ```bash theme={null} kiwifs serve --root ./knowledge ``` KiwiFS pushes to the configured remote on the backup interval. `rebase_before_push` defaults to `true`. You only need to set it when you want the config file to show the behavior explicitly. ## Run a one-shot backup Use `kiwifs backup` to push immediately. ```bash theme={null} kiwifs backup \ --root ./knowledge \ --remote git@github.com:org/knowledge.git \ --branch main ``` The command uses the same rebase-before-push behavior by default. ## What happens before push When `rebase_before_push` is enabled, KiwiFS does this for each backup push: 1. Ensures the local git remote named `backup` points to your configured `remote`. 2. Resolves the backup branch from `[backup].branch`, the current git branch, then `main`. 3. Checks whether the remote branch exists. 4. Fetches `backup/` when the remote branch exists. 5. Refuses automatic rebase if the worktree has uncommitted changes. 6. Rebases clean local commits onto `backup/`. 7. Pushes the branch to the backup remote. 8. Verifies that the remote branch HEAD matches the local HEAD. If the remote branch does not exist yet, KiwiFS skips the rebase and lets the push create it. This supports first-time backup setup. ## Safety behavior KiwiFS automates only the routine, safe path. * It does not force-push. * It only rebases a clean git worktree. * It does not resolve conflicts for you. * If rebase fails, KiwiFS aborts the rebase and records the backup error. * Before rebasing, KiwiFS stores the previous local HEAD at `refs/kiwifs/backup-pre-rebase`. If two writers edit the same lines, resolve the git conflict yourself. KiwiFS will not choose one version over another. ## Disable rebase-before-push Disable this only when another process owns git synchronization and you want KiwiFS to perform a plain push. ```toml theme={null} [backup] rebase_before_push = false ``` ```bash theme={null} KIWI_BACKUP_REBASE_BEFORE_PUSH=false kiwifs serve --root ./knowledge ``` ```bash theme={null} kiwifs backup --root ./knowledge --rebase-before-push=false ``` ## Troubleshooting ### Dirty worktree If KiwiFS reports `worktree has uncommitted changes; refusing automatic rebase`, inspect the repository and save or discard the local changes. ```bash theme={null} cd ./knowledge git status --short git add . git commit -m "chore: save local knowledge changes" kiwifs backup --root . ``` ### Rebase conflict If the rebase conflicts, resolve the conflicted markdown files with normal git tools. ```bash theme={null} cd ./knowledge git status # Edit conflicted files. git add git rebase --continue kiwifs backup --root . ``` Abort if you need to stop and return to the pre-rebase state. ```bash theme={null} git rebase --abort ``` ### Recover the pre-rebase HEAD KiwiFS records the previous local HEAD before rebasing. ```bash theme={null} git show refs/kiwifs/backup-pre-rebase git branch recover-before-backup-rebase refs/kiwifs/backup-pre-rebase ``` # Docker Source: https://docs.kiwifs.com/deploy/docker Deploy KiwiFS with Docker. KiwiFS ships with a multi-stage Dockerfile and a `docker-compose.yml` that includes an optional pgvector sidecar for vector search. ## Quick start Pull and run the pre-built image from Docker Hub. ```bash theme={null} docker run -p 3333:3333 -v ./knowledge:/data ameliaanhlam/kiwifs ``` Build from the repo Dockerfile (includes the latest embedded UI). ```bash theme={null} docker build -t kiwifs . docker run -p 3333:3333 -v ./knowledge:/data kiwifs ``` Your knowledge base is now available at `http://localhost:3333`. ## Dockerfile The Dockerfile uses a three-stage build to produce a minimal runtime image. | Stage | Base image | Purpose | | ---------- | -------------------- | ---------------------------------------------------------- | | 1. Node.js | `node:22-alpine` | Builds the React UI | | 2. Go | `golang:1.26-alpine` | Compiles the Go binary with the UI embedded via `go:embed` | | 3. Runtime | `alpine:3.20` | Minimal image with `git` and `ca-certificates` | The final image contains only the compiled binary, `git` (for versioning), and root CA certificates. ## Docker Compose The included `docker-compose.yml` runs KiwiFS alongside an optional pgvector database for vector search. ```yaml docker-compose.yml theme={null} services: kiwifs: build: . ports: - "3333:3333" # Uncomment to expose additional protocols: # - "2049:2049" # NFS # - "3334:3334" # S3 # - "3335:3335" # WebDAV volumes: - ./knowledge:/data environment: - OPENAI_API_KEY=${OPENAI_API_KEY:-} - KIWI_PGVECTOR_DSN=postgres://kiwi:kiwi@db:5432/kiwi?sslmode=disable depends_on: db: condition: service_healthy db: image: pgvector/pgvector:pg16 environment: POSTGRES_USER: kiwi POSTGRES_PASSWORD: kiwi POSTGRES_DB: kiwi volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U kiwi"] interval: 5s timeout: 3s retries: 5 volumes: pgdata: ``` Start everything with a single command. ```bash theme={null} docker compose up -d ``` ## Configuration ### Environment variables Pass environment variables to configure KiwiFS and enable integrations. ```bash theme={null} docker run \ -v ./knowledge:/data \ -p 3333:3333 \ -e OPENAI_API_KEY=sk-... \ -e KIWI_PGVECTOR_DSN=postgres://user:pass@host:5432/db \ kiwifs ``` In `docker-compose.yml`, add variables under the `environment` key or use an `.env` file. ```yaml theme={null} services: kiwifs: env_file: .env ``` ### Enable authentication Pass auth flags as command arguments. ```bash API key auth theme={null} docker run -v ./knowledge:/data -p 3333:3333 \ kiwifs serve --auth apikey --api-key my-secret-key ``` ```bash OIDC auth theme={null} docker run -v ./knowledge:/data -p 3333:3333 \ kiwifs serve --auth oidc \ --oidc-issuer https://accounts.google.com \ --oidc-client-id my-client-id ``` ### Enable NFS, S3, and WebDAV Uncomment the relevant port mappings in `docker-compose.yml` and add the corresponding flags. ```yaml theme={null} services: kiwifs: build: . ports: - "3333:3333" - "2049:2049" # NFS - "3334:3334" # S3 - "3335:3335" # WebDAV volumes: - ./knowledge:/data command: ["serve", "--nfs", "--s3", "--webdav"] ``` The NFS server runs in userspace (NFSv3) and does not require the container to run in privileged mode. ### Volumes and persistence The knowledge directory must be mounted as a volume so data persists across container restarts. ```yaml theme={null} volumes: - ./knowledge:/data # Knowledge files - pgdata:/var/lib/postgresql/data # Vector search database ``` If you omit the volume mount, all knowledge data is lost when the container stops. ## Health checks KiwiFS exposes health endpoints you can use in your Docker configuration. ```yaml theme={null} services: kiwifs: healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:3333/healthz"] interval: 10s timeout: 3s retries: 3 ``` | Endpoint | Purpose | | ---------- | ------------------ | | `/health` | Basic health check | | `/healthz` | Liveness probe | | `/readyz` | Readiness probe | ## Production tips Pin the image tag in production. Build with a version tag (`docker build -t kiwifs:1.2.0 .`) and reference it explicitly in your compose file. * Set `--async-commit=true` (the default) for better write throughput under load. * Use `--search sqlite` for fast full-text search without external dependencies. * Mount the pgvector volume to a persistent disk if you use vector search. * Set resource limits in your compose file to prevent runaway memory usage. ```yaml theme={null} services: kiwifs: deploy: resources: limits: memory: 512M cpus: "1.0" ``` # Go embed Source: https://docs.kiwifs.com/deploy/go-embed Embed KiwiFS in your Go application. You can embed KiwiFS directly in any Go application using the `pkg/kiwi` package. This gives you a fully functional markdown filesystem server without running a separate process. ## Installation ```bash theme={null} go get github.com/kiwifs/kiwifs/pkg/kiwi ``` ## Quick start Create a KiwiFS server, configure it with options, and start serving. ```go theme={null} package main import ( "log" "github.com/kiwifs/kiwifs/pkg/kiwi" ) func main() { srv, err := kiwi.New("/data/knowledge", kiwi.WithSearch("sqlite"), kiwi.WithVersioning("git"), kiwi.WithAuth("apikey", "my-secret"), ) if err != nil { log.Fatal(err) } defer srv.Close() log.Println("KiwiFS listening on :3333") log.Fatal(srv.ListenAndServe(":3333")) } ``` ## Options Configure KiwiFS behavior by passing option functions to `kiwi.New`. | Option | Description | | ---------------------------------- | --------------------------------------------------------------------------------------------------------------- | | `kiwi.WithSearch(engine)` | Search engine. `"sqlite"` for full-text search or `"grep"` for simple string matching. | | `kiwi.WithVersioning(strategy)` | Version control strategy. `"git"` for git history, `"cow"` for copy-on-write snapshots, or `"none"` to disable. | | `kiwi.WithAuth(authType, key)` | Authentication. Set `authType` to `"apikey"` and provide the key, or `"none"` to disable. | | `kiwi.WithCORSOrigins(origins...)` | Allowed CORS origins. Pass one or more origin URLs. | ```go theme={null} srv, err := kiwi.New("/data/knowledge", kiwi.WithSearch("sqlite"), kiwi.WithVersioning("git"), kiwi.WithAuth("apikey", "my-secret"), kiwi.WithCORSOrigins("https://app.example.com", "http://localhost:3000"), ) ``` ## Methods The `kiwi.Server` type provides these methods. | Method | Signature | Description | | ---------------- | ------------------------------------- | --------------------------------------------------------------------------------------- | | `Handler` | `Handler() http.Handler` | Returns the HTTP handler. Use this to mount KiwiFS alongside other routes. | | `Pipeline` | `Pipeline() *pipeline.Pipeline` | Returns the pipeline for programmatic file operations. | | `ListenAndServe` | `ListenAndServe(addr string) error` | Start the HTTP server on the given address. | | `Shutdown` | `Shutdown(ctx context.Context) error` | Gracefully shut down the server. In-flight requests complete before the method returns. | | `Close` | `Close() error` | Release all resources (search indexes, file watchers, git locks). | ## Mount alongside an existing server Use `srv.Handler()` to mount KiwiFS as a route within your existing HTTP application. ```go theme={null} package main import ( "log" "net/http" "github.com/kiwifs/kiwifs/pkg/kiwi" ) func main() { // Create the KiwiFS server kiwifsSrv, err := kiwi.New("/data/knowledge", kiwi.WithSearch("sqlite"), kiwi.WithVersioning("git"), ) if err != nil { log.Fatal(err) } defer kiwifsSrv.Close() // Set up your application router mux := http.NewServeMux() // Mount KiwiFS under /knowledge/ mux.Handle("/knowledge/", http.StripPrefix("/knowledge", kiwifsSrv.Handler())) // Your own routes mux.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("ok")) }) log.Println("App listening on :8080") log.Fatal(http.ListenAndServe(":8080", mux)) } ``` When mounting under a subpath, use `http.StripPrefix` so KiwiFS receives paths relative to its root. ## Programmatic file operations Use `srv.Pipeline()` to read and write files without going through HTTP. ```go theme={null} pipe := srv.Pipeline() // Write a file err := pipe.Write("concepts/auth.md", []byte(`--- title: Authentication status: draft --- # Authentication Overview of auth mechanisms. `)) // Read a file content, err := pipe.Read("concepts/auth.md") // List files files, err := pipe.List("concepts/") ``` The pipeline respects the same versioning and indexing rules as the HTTP API. Writes trigger git commits (if versioning is enabled) and update the search index automatically. ## Graceful shutdown Handle OS signals to shut down KiwiFS cleanly. This flushes pending async commits and releases file locks. ```go theme={null} package main import ( "context" "log" "os" "os/signal" "syscall" "time" "github.com/kiwifs/kiwifs/pkg/kiwi" ) func main() { srv, err := kiwi.New("/data/knowledge", kiwi.WithSearch("sqlite"), kiwi.WithVersioning("git"), ) if err != nil { log.Fatal(err) } // Start server in a goroutine go func() { if err := srv.ListenAndServe(":3333"); err != nil { log.Printf("server stopped: %v", err) } }() // Wait for interrupt signal quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit // Graceful shutdown with timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Printf("shutdown error: %v", err) } srv.Close() log.Println("server stopped") } ``` Always call `srv.Close()` or `srv.Shutdown(ctx)` before your program exits. Skipping this can leave stale git lock files or incomplete search indexes. # Kubernetes Source: https://docs.kiwifs.com/deploy/kubernetes Deploy KiwiFS on Kubernetes. This guide covers deploying KiwiFS to a Kubernetes cluster with persistent storage, configuration, and health checks. ## Basic deployment Create a Deployment and Service to run KiwiFS in your cluster. ```yaml kiwifs-deployment.yaml theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: kiwifs labels: app: kiwifs spec: replicas: 1 selector: matchLabels: app: kiwifs template: metadata: labels: app: kiwifs spec: containers: - name: kiwifs image: kiwifs:latest args: ["serve", "--root", "/data", "--port", "3333"] ports: - containerPort: 3333 name: http volumeMounts: - name: knowledge mountPath: /data livenessProbe: httpGet: path: /healthz port: http initialDelaySeconds: 5 periodSeconds: 10 readinessProbe: httpGet: path: /readyz port: http initialDelaySeconds: 3 periodSeconds: 5 resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "512Mi" cpu: "1000m" volumes: - name: knowledge persistentVolumeClaim: claimName: kiwifs-data ``` ```yaml kiwifs-service.yaml theme={null} apiVersion: v1 kind: Service metadata: name: kiwifs spec: selector: app: kiwifs ports: - port: 3333 targetPort: http protocol: TCP name: http type: ClusterIP ``` ## Persistent storage Use a PersistentVolumeClaim to store your knowledge base across pod restarts and rescheduling. ```yaml kiwifs-pvc.yaml theme={null} apiVersion: v1 kind: PersistentVolumeClaim metadata: name: kiwifs-data spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: standard ``` Use `ReadWriteOnce` access mode. KiwiFS manages its own concurrency and should not have multiple pods writing to the same volume simultaneously. ## Configuration ### ConfigMap Store your KiwiFS configuration in a ConfigMap and mount it into the pod. ```yaml kiwifs-configmap.yaml theme={null} apiVersion: v1 kind: ConfigMap metadata: name: kiwifs-config data: config.toml: | [search] engine = "sqlite" [versioning] strategy = "git" [async] enabled = true batch_window_ms = 200 batch_max_size = 50 ``` Add the ConfigMap volume to your Deployment. ```yaml theme={null} spec: template: spec: containers: - name: kiwifs volumeMounts: - name: knowledge mountPath: /data - name: config mountPath: /data/.kiwi volumes: - name: knowledge persistentVolumeClaim: claimName: kiwifs-data - name: config configMap: name: kiwifs-config ``` ### Secrets Store sensitive values like API keys and connection strings in a Secret. ```yaml kiwifs-secret.yaml theme={null} apiVersion: v1 kind: Secret metadata: name: kiwifs-secrets type: Opaque stringData: api-key: "your-secret-api-key" openai-api-key: "sk-..." pgvector-dsn: "postgres://user:pass@pgvector:5432/kiwi?sslmode=disable" ``` Reference the Secret as environment variables in the Deployment. ```yaml theme={null} spec: template: spec: containers: - name: kiwifs args: ["serve", "--root", "/data", "--auth", "apikey"] env: - name: KIWIFS_API_KEY valueFrom: secretKeyRef: name: kiwifs-secrets key: api-key - name: OPENAI_API_KEY valueFrom: secretKeyRef: name: kiwifs-secrets key: openai-api-key - name: KIWI_PGVECTOR_DSN valueFrom: secretKeyRef: name: kiwifs-secrets key: pgvector-dsn ``` ## Health check probes KiwiFS exposes three health endpoints. | Endpoint | Purpose | Use as | | ---------- | --------------------- | ---------------- | | `/health` | General health status | Informational | | `/healthz` | Liveness check | `livenessProbe` | | `/readyz` | Readiness check | `readinessProbe` | The liveness probe restarts the pod if KiwiFS becomes unresponsive. The readiness probe removes the pod from service endpoints until it finishes initializing and indexing. ```yaml theme={null} livenessProbe: httpGet: path: /healthz port: http initialDelaySeconds: 5 periodSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: path: /readyz port: http initialDelaySeconds: 3 periodSeconds: 5 failureThreshold: 2 ``` ## NFS export for native volume mounts KiwiFS includes a userspace NFSv3 server. You can use it to expose your knowledge base as a native NFS volume to other pods in the cluster. The NFS server runs entirely in userspace. It does not require privileged mode or host kernel NFS support. ### Step 1: Enable NFS on the KiwiFS pod Add the `--nfs` flag and expose port 2049. ```yaml theme={null} spec: template: spec: containers: - name: kiwifs args: - "serve" - "--root" - "/data" - "--nfs" - "--nfs-allow" - "10.0.0.0/8" ports: - containerPort: 3333 name: http - containerPort: 2049 name: nfs ``` Create a Service for the NFS port. ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: kiwifs-nfs spec: selector: app: kiwifs ports: - port: 2049 targetPort: nfs protocol: TCP type: ClusterIP ``` ### Step 2: Mount from other pods Other pods can mount the NFS export as a standard NFS volume. ```yaml theme={null} spec: volumes: - name: knowledge nfs: server: kiwifs-nfs path: / containers: - name: app volumeMounts: - name: knowledge mountPath: /knowledge readOnly: true ``` Use `readOnly: true` on consumer pods to prevent accidental writes. Route all writes through the KiwiFS HTTP API instead. ## Multi-space setup Run multiple knowledge spaces by mounting separate PVCs and registering them with the `--space` flag. ```yaml theme={null} spec: template: spec: containers: - name: kiwifs args: - "serve" - "--root" - "/data/default" - "--space" - "team=/data/team" - "--space" - "docs=/data/docs" volumeMounts: - name: default-data mountPath: /data/default - name: team-data mountPath: /data/team - name: docs-data mountPath: /data/docs volumes: - name: default-data persistentVolumeClaim: claimName: kiwifs-default - name: team-data persistentVolumeClaim: claimName: kiwifs-team - name: docs-data persistentVolumeClaim: claimName: kiwifs-docs ``` Each space operates independently with its own directory, versioning history, and search index. ## Ingress Expose KiwiFS outside the cluster with an Ingress resource. ```yaml kiwifs-ingress.yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: kiwifs annotations: nginx.ingress.kubernetes.io/proxy-body-size: "50m" spec: rules: - host: kiwi.example.com http: paths: - path: / pathType: Prefix backend: service: name: kiwifs port: number: 3333 tls: - hosts: - kiwi.example.com secretName: kiwifs-tls ``` Set `proxy-body-size` high enough to handle file uploads. The default nginx limit of 1MB is too low for most knowledge bases. # Ollama vector search on a small VPS Source: https://docs.kiwifs.com/deploy/ollama-vector-search Run semantic search with a local Ollama embedder, sqlite-vec, and low-concurrency vector indexing. Use this setup when you want semantic search without an external embedding API. It is useful on small CPU-only VPS instances where Ollama can be slower than the default vector indexing settings. KiwiFS supports two tuning knobs for this case: * `[search.vector].worker_count` lowers concurrent embed and upsert workers. * `[search.vector.embedder].timeout` raises the Ollama HTTP request timeout. The defaults stay optimized for normal machines: `worker_count` defaults to `5`, and the Ollama timeout defaults to `30s`. ## Prerequisites * KiwiFS with the vector tuning options from [kiwifs/kiwifs#20](https://github.com/kiwifs/kiwifs/pull/20). * An Ollama server reachable from the KiwiFS process. * An embedding model pulled into Ollama. ```bash theme={null} ollama pull nomic-embed-text ``` ## Configure KiwiFS Create or update `.kiwi/config.toml` in your knowledge root. ```toml theme={null} [search] engine = "sqlite" [search.vector] enabled = true worker_count = 1 [search.vector.embedder] provider = "ollama" base_url = "http://127.0.0.1:11434" model = "nomic-embed-text" timeout = "120s" [search.vector.store] provider = "sqlite-vec" ``` This keeps the full stack local: * Ollama generates embeddings on the same machine. * `sqlite-vec` stores vectors inside the KiwiFS search database. * No embedding API key is required. Use Go duration strings for `timeout`, such as `60s`, `120s`, or `3m`. ## Choose the Ollama URL The correct `base_url` depends on where KiwiFS runs. Use the local Ollama URL. ```toml theme={null} [search.vector.embedder] provider = "ollama" base_url = "http://127.0.0.1:11434" model = "nomic-embed-text" timeout = "120s" ``` On Linux, add a host gateway entry to the KiwiFS container and use `host.docker.internal`. ```yaml theme={null} services: kiwifs: image: ameliaanhlam/kiwifs:latest extra_hosts: - "host.docker.internal:host-gateway" ``` Then configure KiwiFS: ```toml theme={null} [search.vector.embedder] provider = "ollama" base_url = "http://host.docker.internal:11434" model = "nomic-embed-text" timeout = "120s" ``` Use the Ollama service name as the hostname. ```toml theme={null} [search.vector.embedder] provider = "ollama" base_url = "http://ollama:11434" model = "nomic-embed-text" timeout = "120s" ``` ## Index existing files After enabling vector search, rebuild the search indexes once. ```bash theme={null} kiwifs reindex --root ./knowledge ``` For a large vault on a small VPS, keep `worker_count = 1` for the first reindex. Raise it later only if CPU, memory, and Ollama latency remain stable. ## When to tune the values | Symptom | Change | | -------------------------------------------------------------------- | --------------------------------------------------------------------------- | | Ollama requests time out during reindexing | Increase `timeout`, for example from `120s` to `3m` | | CPU stays saturated or the VPS becomes unresponsive | Lower `worker_count` to `1` | | Reindexing is stable but too slow | Try `worker_count = 2` | | Vector search works, but new writes appear in semantic search slowly | Keep `worker_count` low and run `kiwifs reindex` during low-traffic periods | ## Troubleshooting ### Check Ollama from the KiwiFS machine ```bash theme={null} curl http://127.0.0.1:11434/api/tags ``` If KiwiFS runs in Docker, run the check from inside the container and use the same hostname as `base_url`. ```bash theme={null} curl http://host.docker.internal:11434/api/tags ``` ### Confirm the model exists ```bash theme={null} ollama list ``` If `nomic-embed-text` is missing, pull it: ```bash theme={null} ollama pull nomic-embed-text ``` ### Recover from a failed first reindex Keep the same config, raise `timeout`, and run reindex again. The vector index is derivative data, so KiwiFS can rebuild it from the markdown files. ```bash theme={null} kiwifs reindex --root ./knowledge ``` # Document export Source: https://docs.kiwifs.com/export/documents Render markdown to PDF, HTML, slides, or a static site. Beyond JSONL and CSV, KiwiFS can **render** markdown into deliverable formats using Pandoc, Marp, and MkDocs tooling (bundled in the Docker image). ## Endpoint ```http theme={null} POST /api/kiwi/export/document Content-Type: application/json ``` ## Request body Output format: `pdf`, `html`, `slides`, or `site`. File or directory under the knowledge root to render. Theme name for HTML/PDF output. Produce a single-file HTML with embedded assets. BibTeX path for citations. Citation style preset: `apa`, `ieee`, `chicago`, `vancouver`, or `harvard`. PDF engine: `typst` or `xelatex` (default: auto-detect). Slide format (Marp). Static site title (for `site` format). Canonical site URL. Edit-on-GitHub style link for the generated site. ## Examples ```bash theme={null} kiwifs export --format pdf --path docs/report.md --output report.pdf --theme paper kiwifs export --format html --path concepts/ --self-contained --output site.html kiwifs export --format slides --path talks/intro.md --output slides.html kiwifs export --format site --path docs/ --site-name "My Wiki" --output docs-site.zip ``` ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/export/document' \ -H 'Content-Type: application/json' \ -d '{"format":"pdf","path":"pages/report.md"}' \ -o report.pdf ``` ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/export/document' \ -H 'Content-Type: application/json' \ -d '{"format":"html","path":"concepts/","self_contained":true}' \ -o site.html ``` ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/export/document' \ -H 'Content-Type: application/json' \ -d '{"format":"slides","path":"talks/intro.md","slide_format":"marp"}' \ -o intro.pdf ``` The response streams the rendered artifact with an appropriate `Content-Type`. Large exports may take up to **five minutes** server-side. ## MCP **`kiwi_export_document`** accepts the same options for agent-driven publishing workflows. ## Docker requirement PDF and site generation depend on external tools bundled in the official Docker image. Bare-metal installs may need Pandoc and related dependencies installed separately. ## Related documentation JSONL, CSV, and Parquet export. REST reference. # Data export Source: https://docs.kiwifs.com/export/overview Export your knowledge base as JSONL, CSV, or Parquet. KiwiFS can export your entire knowledge base (or a subset of it) as JSONL, CSV, or Parquet. You can export from the CLI or the REST API. ## Formats Each line is a JSON object representing one file. Fields come from frontmatter. ```json theme={null} {"path":"concepts/auth.md","title":"Authentication","status":"published","updated":"2026-04-20T10:00:00Z"} ``` With `--include-content`, each object includes the full markdown body. ```json theme={null} {"path":"concepts/auth.md","title":"Authentication","status":"published","content":"# Authentication\n\nOverview of auth mechanisms.\n"} ``` With `--include-links`, each object includes link data. ```json theme={null} {"path":"concepts/auth.md","title":"Authentication","outgoing_links":["concepts/oauth.md","concepts/jwt.md"],"incoming_links":["guides/security.md"]} ``` With `--include-embeddings`, each object includes a vector embedding array. A `.schema.json` sidecar file is also written alongside the output file, describing the embedding model and dimensions. ```json theme={null} {"path":"concepts/auth.md","title":"Authentication","embedding":[0.0123,-0.0456,0.0789]} ``` Columnar export for data warehouses and analytics pipelines. Supports the same flags as JSONL (`--include-content`, `--include-links`, `--include-embeddings`, `--path`, `--limit`). ```bash theme={null} kiwifs export --format parquet --include-content -o knowledge.parquet ``` Standard CSV with one row per file. The first row contains headers. Specify which frontmatter fields to include with `--columns`. ```csv theme={null} path,name,status,grade students/alice.md,Alice,active,A students/bob.md,Bob,active,B+ students/carol.md,Carol,graduated,A- ``` ## CLI usage Use the `kiwifs export` command to export from the command line. ```bash Export all files as JSONL theme={null} kiwifs export --format jsonl --output knowledge.jsonl ``` ```bash Export with full content theme={null} kiwifs export --format jsonl --include-content --output full.jsonl ``` ```bash Export a subdirectory as CSV theme={null} kiwifs export --format csv --path students/ --columns name,status,grade -o students.csv ``` ```bash Export as Parquet theme={null} kiwifs export --format parquet --include-content -o knowledge.parquet ``` ```bash Pipe to jq theme={null} kiwifs export --format jsonl | jq '.title' ``` ```bash Include vector embeddings theme={null} kiwifs export --format jsonl --include-embeddings -o vectors.jsonl ``` ### CLI flags | Flag | Short | Default | Description | | ---------------------- | ----- | ------------- | --------------------------------------------- | | `--root` | `-r` | `./knowledge` | Knowledge root directory | | `--format` | | `jsonl` | Output format: `jsonl`, `csv`, or `parquet` | | `--output` | `-o` | stdout | Output file path | | `--path` | | | Scope export to a subdirectory | | `--columns` | | | Comma-separated frontmatter fields to include | | `--include-content` | | `false` | Include the full markdown body | | `--include-links` | | `false` | Include outgoing and incoming link arrays | | `--include-embeddings` | | `false` | Include vector embeddings | | `--limit` | | `0` | Max files to export (0 = unlimited) | ## REST API Export your knowledge base over HTTP using the `/api/kiwi/export` endpoint. ```bash JSONL with content theme={null} curl "http://localhost:3333/api/kiwi/export?format=jsonl&include-content=true" \ -o knowledge.jsonl ``` ```bash CSV with specific columns theme={null} curl "http://localhost:3333/api/kiwi/export?format=csv&columns=name,status&path=students/" \ -o students.csv ``` ```bash With API key auth theme={null} curl "http://localhost:3333/api/kiwi/export?format=jsonl" \ -H "Authorization: Bearer my-api-key" \ -o knowledge.jsonl ``` ### Query parameters | Parameter | Default | Description | | -------------------- | ------- | ------------------------------------------- | | `format` | `jsonl` | Output format: `jsonl`, `csv`, or `parquet` | | `path` | | Scope to a subdirectory | | `columns` | | Comma-separated frontmatter fields | | `include-content` | `false` | Include markdown body | | `include-links` | `false` | Include link arrays | | `include-embeddings` | `false` | Include vector embeddings | | `limit` | `0` | Max files (0 = unlimited) | The response streams as `application/x-ndjson` (JSONL) or `text/csv` (CSV) with chunked transfer encoding. You can pipe the response directly into downstream tools. ## Embeddings sidecar When you export with `--include-embeddings`, KiwiFS writes a `.schema.json` sidecar file next to the output file. This file describes the embedding model used and the vector dimensions. ```json vectors.schema.json theme={null} { "embedding_model": "text-embedding-3-small", "dimensions": 1536, "distance_metric": "cosine" } ``` Embeddings are only available if you have configured a vector search provider (such as pgvector with an OpenAI API key). ## Common recipes Export frontmatter as JSONL and commit it alongside your knowledge base for a searchable metadata snapshot. ```bash theme={null} kiwifs export --format jsonl -o .kiwi/metadata.jsonl cd knowledge && git add .kiwi/metadata.jsonl && git commit -m "Update metadata export" ``` Export with content and embeddings, then feed the output into your static site search pipeline. ```bash theme={null} kiwifs export --format jsonl --include-content --include-embeddings -o search-index.jsonl ``` Export as CSV and load into your warehouse using standard ETL tools. ```bash theme={null} kiwifs export --format csv --columns title,status,updated,author -o export.csv # Load into your warehouse bq load --source_format=CSV mydataset.knowledge export.csv ``` Pipe JSONL output to `jq` for ad-hoc filtering and transformation. ```bash theme={null} # Find all draft documents kiwifs export --format jsonl | jq 'select(.status == "draft")' # Extract just titles and paths kiwifs export --format jsonl | jq '{path, title}' # Count documents by status kiwifs export --format jsonl | jq -s 'group_by(.status) | map({status: .[0].status, count: length})' ``` For large knowledge bases, use `--path` to scope the export to a subdirectory and `--limit` to cap the number of files. This keeps export times predictable. ## Document export (PDF, HTML, slides) To render markdown into PDF, HTML, slide decks, or static sites, use [Document export](/export/documents) (`POST /api/kiwi/export/document` or MCP `kiwi_export_document`). # Alternate protocols Source: https://docs.kiwifs.com/guides/alternate-protocols Mount and integrate KiwiFS over NFS, S3, WebDAV, and FUSE alongside REST and MCP. Every access path flows through the **same storage layer**: atomic write → git commit → search index → SSE broadcast. ## Quick comparison | Protocol | Port | Best for | | -------- | --------------- | ---------------------------------------- | | REST | `3333` | Web UI, curl, CI scripts | | MCP | stdio or `8181` | Claude, Cursor, custom agents | | NFS | `2049` | Docker / Kubernetes mounts | | S3 | `3334` | Backup tools, ETL, S3-compatible clients | | WebDAV | `3335` | Desktop sync, mapped drives | | FUSE | — | Developer workstation remote mount | ## Enable on serve ```bash theme={null} kiwifs serve --root ./knowledge \ --nfs --nfs-allow 10.0.0.0/8 \ --s3 \ --webdav ``` Mount from Linux or Kubernetes: ```bash theme={null} sudo mount -t nfs -o vers=3 localhost:/ /mnt/kiwi ``` Agents use normal shell tools: ```bash theme={null} cat /mnt/kiwi/pages/auth.md grep -r "timeout" /mnt/kiwi/ echo "# Note" >> /mnt/kiwi/episodes/session.md ``` NFS supports symlinks, open-then-delete semantics, and advisory locks. Internal dirs (`.git`, `.kiwi`) are hidden from listings. Restrict access with `--nfs-allow ` in production. Point any S3 client at `http://localhost:3334`: ```bash theme={null} aws s3 sync s3://knowledge/ /backup/ --endpoint-url http://localhost:3334 ``` Useful for `aws s3 sync` backups or S3-compatible ETL pipelines. Connect from macOS **Finder → Connect to Server** or Windows mapped drive: ``` http://localhost:3335/ ``` WebDAV uses buffered writes with spill-to-disk for large uploads. Mount a **remote** KiwiFS as a local folder: ```bash theme={null} kiwifs mount --remote http://host:3333 --mountpoint ~/kiwi-mount ``` Optional auth: ```bash theme={null} export KIWIFS_API_KEY=my-secret kiwifs mount --remote http://host:3333 --mountpoint ~/kiwi-mount ``` FUSE reports sub-second mtime and supports symlinks. **mmap** is not available (HTTP-backed I/O). ## POSIX semantics | Access path | POSIX level | | -------------------- | ---------------------- | | Local knowledge root | Full | | NFS mount | Near-full | | FUSE mount | Near-full (no mmap) | | WebDAV | Partial | | REST / S3 / MCP | HTTP or tool semantics | See [POSIX guide](/guides/posix) for the full matrix. Run alternate protocol ports behind a firewall in production. NFS, S3, and WebDAV endpoints are sensitive network surfaces. ## Related documentation Protocol overview. Full `kiwifs serve` reference. Compliance details. SSE broadcast. # Authentication and tokens Source: https://docs.kiwifs.com/guides/auth-tokens Configure API authentication — global keys, per-space tokens, and OIDC — and manage tokens via the CLI. KiwiFS supports four authentication modes. Choose the one that fits your deployment. ## Auth modes No authentication. Suitable for local development and trusted networks. ```toml .kiwi/config.toml theme={null} [auth] type = "none" ``` A single global API key protects all endpoints. ```toml .kiwi/config.toml theme={null} [auth] type = "apikey" api_key = "kiwi_sk_your_secret_here" ``` Pass the key as a Bearer token: ```bash theme={null} curl http://localhost:3333/api/kiwi/tree \ -H "Authorization: Bearer kiwi_sk_your_secret_here" ``` Each space gets its own scoped API key with fine-grained permissions. ```toml .kiwi/config.toml theme={null} [auth] type = "perspace" [[auth.api_keys]] key = "kiwi_ro_eng_abc123" space = "engineering" actor = "eng-reader" scope = "read" prefix = "" [[auth.api_keys]] key = "kiwi_rw_eng_xyz789" space = "engineering" actor = "eng-writer" scope = "write" prefix = "concepts/" [[auth.api_keys]] key = "kiwi_admin_all" space = "" actor = "admin" scope = "admin" ``` OpenID Connect for SSO via your identity provider. ```toml .kiwi/config.toml theme={null} [auth] type = "oidc" [auth.oidc] issuer = "https://accounts.google.com" client_id = "your-client-id" ``` Clients pass a JWT Bearer token from the identity provider. Auth configuration is hot-reloadable — send `SIGHUP` to the server process to reload from disk without restarting. ## Per-space token fields When using `perspace` auth, each `[[auth.api_keys]]` entry supports: | Field | Required | Description | | -------- | -------- | --------------------------------------------------- | | `key` | Yes | The API key string (shown once on creation via CLI) | | `space` | No | Restrict to a specific space (empty = all spaces) | | `actor` | No | Default `X-Actor` value for git attribution | | `scope` | Yes | Permission level: `read`, `write`, or `admin` | | `prefix` | No | Restrict access to paths under this prefix | ### Scopes | Scope | Permissions | | ------- | ------------------------------------------------------------------ | | `read` | `GET` endpoints only — tree, file, search, query, analytics | | `write` | Read + `PUT`, `POST`, `DELETE` on files, drafts, canvas, workflows | | `admin` | Write + space management, webhook CRUD, schema changes, audit log | ## Managing tokens via CLI The `kiwifs token` command creates and manages API keys stored in `.kiwi/config.toml`. ### Create a token ```bash theme={null} kiwifs token create --root ./knowledge --space docs --scope read --actor ci-reader ``` The plaintext key is displayed once. Copy it immediately — it cannot be retrieved later. ``` Created API key: kiwi_ro_abc1deadbeef... Space: docs | Scope: read | Actor: ci-reader ``` ### List tokens ```bash theme={null} kiwifs token list --root ./knowledge ``` ``` PREFIX SPACE SCOPE ACTOR kiwi_ro_abc1 docs read ci-reader kiwi_rw_xyz7 engineering write eng-bot kiwi_admin_a (all) admin admin ``` ### Revoke a token ```bash theme={null} kiwifs token revoke kiwi_ro_abc1 ``` Revocation removes the key from `.kiwi/config.toml`. Active requests with the revoked key are rejected on the next config reload. Revoking a token does not require a server restart — KiwiFS watches the config file for changes. ## Path-scoped access Use the `prefix` field to restrict a token to a specific directory tree: ```toml theme={null} [[auth.api_keys]] key = "kiwi_rw_agent_onboarding" space = "engineering" actor = "onboarding-bot" scope = "write" prefix = "onboarding/" ``` This token can only read and write files under `onboarding/`. Requests to other paths return `401`. ## Cloud authentication KiwiFS Cloud uses a separate authentication system: 1. **API key** — pass `kiwi_sk_*` as a Bearer token 2. **MCP OAuth 2.1** — browser-based login via WorkOS (PKCE) ```bash theme={null} kiwifs login # OAuth device flow kiwifs whoami # Show current identity kiwifs connect my-workspace --write cursor # Generate MCP config ``` Cloud credentials are stored at `~/.kiwifs/credentials.json`. See [KiwiFS Cloud](/guides/cloud) for details. ## Related documentation Full auth config reference. Auth headers and error codes. Per-space routing and access control. Token create, list, and revoke commands. # KiwiFS Cloud Source: https://docs.kiwifs.com/guides/cloud Connect to hosted KiwiFS workspaces — authentication, REST API, MCP, and CLI setup. [KiwiFS Cloud](https://app.kiwifs.com) is the hosted version of KiwiFS. It handles workspace provisioning, authentication, billing, and multi-tenant routing so you can skip deployment and go straight to building. ## How it works ``` Your agent / app KiwiFS Cloud ───────────────── ────────────────────── kiwi_sk_* or OAuth ──────────▶ api.kiwifs.com (proxy) │ ▼ KiwiFS instance (your workspace) ``` Cloud authenticates your request and forwards it to the KiwiFS instance that backs your workspace. All self-hosted KiwiFS features — REST, MCP, search, versioning, drafts, and more — are available through the cloud proxy. ## Authentication Every workspace has a `kiwi_sk_*` API key. Pass it as a Bearer token: ```bash theme={null} curl https://api.kiwifs.com/api/workspaces/eng-handbook/file?path=README.md \ -H "Authorization: Bearer $KIWI_API_KEY" ``` Get your key from **Settings → API Key** in the workspace UI, or via the CLI: ```bash theme={null} kiwifs login kiwifs connect eng-handbook ``` For interactive agent sessions (Claude Desktop, Cursor, etc.), KiwiFS Cloud supports MCP OAuth 2.1 with PKCE. The agent opens a browser window for login — no API key needed. MCP clients that support OAuth will automatically discover the flow via `WWW-Authenticate` headers. ## REST API Base URL: `https://api.kiwifs.com/api/workspaces/{slug}` The cloud proxy exposes the same endpoints as self-hosted KiwiFS. Key endpoints: | Method | Path | Description | | -------- | ---------------------- | ----------------------- | | `GET` | `/tree` | List workspace files | | `GET` | `/file?path=README.md` | Read a file | | `PUT` | `/file?path=README.md` | Create or update a file | | `DELETE` | `/file?path=README.md` | Delete a file | | `GET` | `/search?q=auth` | Full-text search | | `POST` | `/search/semantic` | Vector search | | `GET` | `/query?q=TABLE ...` | DQL query | | `GET` | `/versions?path=...` | Git history | | `GET` | `/context` | Agent playbook + schema | ```bash curl theme={null} curl "https://api.kiwifs.com/api/workspaces/eng-handbook/file?path=README.md" \ -H "Authorization: Bearer $KIWI_API_KEY" ``` ```javascript JavaScript theme={null} const res = await fetch( "https://api.kiwifs.com/api/workspaces/eng-handbook/file?path=README.md", { headers: { Authorization: `Bearer ${process.env.KIWI_API_KEY}` } } ); const content = await res.text(); ``` ```python Python theme={null} import os, requests res = requests.get( "https://api.kiwifs.com/api/workspaces/eng-handbook/file", params={"path": "README.md"}, headers={"Authorization": f"Bearer {os.environ['KIWI_API_KEY']}"}, ) content = res.text ``` For the full endpoint reference, see the [REST API documentation](/api/overview). All endpoints documented there work under the cloud base URL. ## MCP The MCP endpoint for a cloud workspace is: ``` https://api.kiwifs.com/api/workspaces/{slug}/mcp ``` For agents that support MCP OAuth (Claude Desktop, Cursor, etc.): ```json theme={null} { "mcpServers": { "kiwi": { "url": "https://api.kiwifs.com/api/workspaces/eng-handbook/mcp" } } } ``` The agent will open a browser login flow on first connection. For environments without a browser: ```json theme={null} { "mcpServers": { "kiwi": { "url": "https://api.kiwifs.com/api/workspaces/eng-handbook/mcp", "headers": { "Authorization": "Bearer kiwi_sk_..." } } } } ``` Generate and write MCP config automatically: ```bash theme={null} kiwifs login kiwifs connect eng-handbook ``` This writes the appropriate config to your MCP client's config file. All 60+ MCP tools are available through the cloud endpoint. See [MCP tool reference](/concepts/mcp). ## CLI setup ```bash theme={null} # Authenticate with KiwiFS Cloud kiwifs login # Generate MCP config for a workspace kiwifs connect # Check who you're logged in as kiwifs whoami # Log out kiwifs logout ``` `kiwifs connect` detects your MCP client (Claude Desktop, Cursor, etc.) and writes the appropriate config file. ## API key management | Operation | How | | ---------- | --------------------------------------------------------------------------- | | View key | **Settings → API Key** in workspace UI | | Rotate key | **Settings → API Key → Rotate** or `POST /api/workspaces/{slug}/rotate-key` | | Scope | Keys are scoped to a single workspace | | Format | `kiwi_sk_` followed by 32 URL-safe characters | API keys are shown once on creation or rotation. Store them securely — KiwiFS Cloud stores only a hash and cannot recover the plaintext. ## Public workspaces Workspaces can be set to `public` or `unlisted` visibility. Public workspaces expose read-only endpoints without authentication: ```bash theme={null} # Public tree GET /api/workspaces/{slug}/public/tree # Public file read GET /api/workspaces/{slug}/public/file?path=README.md # Public search (public workspaces only) GET /api/workspaces/{slug}/public/search?q=auth # Published page (any visibility) GET /api/workspaces/{slug}/public/page/{path} ``` ## Differences from self-hosted | Feature | Self-hosted | Cloud | | ------------------- | ------------------------------------------- | ---------------------------------------------- | | Auth | configurable (none, apikey, perspace, OIDC) | API key + MCP OAuth 2.1 | | Base URL | `http://host:3333/api/kiwi` | `https://api.kiwifs.com/api/workspaces/{slug}` | | Multi-space | `X-Kiwi-Space` header or URL prefix | one workspace per slug | | Alternate protocols | NFS, S3, WebDAV, FUSE | MCP and REST only | | OpenAPI spec | `/api/openapi.json` | not exposed (use self-hosted spec) | | Billing | N/A | managed via Settings → Billing | ## Related Full REST API reference (endpoints work the same on cloud). 60+ MCP tools and client setup. Agent onboarding with kiwi\_context. Self-hosted quickstart for local development. # Examples Source: https://docs.kiwifs.com/guides/examples Practical workflows for agents, DQL, import/export, and deployment. ## LLM wiki pattern ```bash theme={null} kiwifs init --template knowledge --root ./knowledge ``` ```text Directory structure theme={null} knowledge/ ├── SCHEMA.md ├── index.md ├── log.md ├── pages/ ├── episodes/ └── .kiwi/playbook.md ``` Call **`kiwi_context`** on connect, then follow playbook operations. See [Agent playbook](/concepts/agent-playbook). ## Agent writes ```bash theme={null} curl -X PUT 'http://localhost:3333/api/kiwi/file?path=pages/auth.md' \ -H 'X-Actor: my-agent' \ -H 'X-Provenance: run:run-249' \ -d '# Authentication OAuth2 + JWT.' ``` ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/bulk' \ -H 'Content-Type: application/json' \ -d '{ "files": [ {"path": "pages/auth.md", "content": "# Auth\n\nUpdated."}, {"path": "log.md", "content": "- 2026-05-21: updated auth"} ], "actor": "agent:exec_abc", "message": "run 249" }' ``` ```bash theme={null} echo "# Finding" > /mnt/kiwi/pages/finding-042.md ``` ```bash theme={null} curl 'http://localhost:3333/api/kiwi/meta?where=$.derived-from[*].id=run-249' ``` ## MCP tool sequences ``` kiwi_context ``` ``` kiwi_search("authentication") kiwi_write("pages/auth.md", content) kiwi_read("index.md") kiwi_write("index.md", updated) kiwi_append("log.md", "- added auth") ``` ``` kiwi_search("payment timeout") kiwi_read("pages/payments.md") ``` ``` kiwi_append("episodes/2026-05-21.md", observation) ``` ``` kiwi_memory_report kiwi_read("episodes/2026-05-21.md") kiwi_write("pages/topic.md", merged_content_with_merged_from) ``` See [Episodic memory](/concepts/episodic-memory). ``` kiwi_analytics kiwi_health_check("pages/auth.md") kiwi_lint ``` ## DQL ```bash Table query theme={null} kiwifs query 'TABLE title, status FROM "pages" WHERE status = "draft" SORT priority DESC' ``` ```bash Count by group theme={null} kiwifs query 'COUNT FROM "pages" GROUP BY status' ``` ```bash REST theme={null} curl 'http://localhost:3333/api/kiwi/query?q=TABLE%20title%20FROM%20%22pages%22' ``` ## Aggregation ```bash theme={null} kiwifs aggregate --group status --calc count,avg:priority ``` ## Import ```bash From PostgreSQL theme={null} kiwifs import --from postgres --dsn "postgres://..." --table users --root ./knowledge ``` ```bash From Obsidian theme={null} kiwifs import --from obsidian --path ~/vault --root ./knowledge ``` ```bash Dry run theme={null} kiwifs import --from csv --path data.csv --root ./knowledge --dry-run ``` ## Export ```bash JSONL with content theme={null} kiwifs export --format jsonl --include-content -o knowledge.jsonl ``` ```bash With embeddings theme={null} kiwifs export --format jsonl --include-embeddings -o vectors.jsonl ``` ```bash Document render (PDF) theme={null} curl -X POST 'http://localhost:3333/api/kiwi/export/document' \ -H 'Content-Type: application/json' \ -d '{"format":"pdf","path":"pages/report.md"}' -o report.pdf ``` See [Document export](/export/documents). ## Production config ```toml .kiwi/config.toml theme={null} [server] public_url = "https://wiki.example.com" [search] engine = "sqlite" [search.vector] enabled = true [search.vector.embedder] provider = "ollama" model = "nomic-embed-text" [versioning] strategy = "git" [backup] remote = "git@github.com:org/knowledge.git" interval = "5m" ``` ## Docker ```bash theme={null} docker run -d --restart always \ -v /data/knowledge:/data \ -p 3333:3333 \ ameliaanhlam/kiwifs serve --root /data ``` ## Go embed ```go theme={null} import "github.com/kiwifs/kiwifs/pkg/kiwi" srv, err := kiwi.New("/data/knowledge", kiwi.WithSearch("sqlite")) mux.Handle("/wiki/", http.StripPrefix("/wiki", srv.Handler())) ``` See [Go embed](/deploy/go-embed) and [Alternate protocols](/guides/alternate-protocols). # FAQ Source: https://docs.kiwifs.com/guides/faq Frequently asked questions about KiwiFS installation, agents, search, and deployment. A markdown filesystem for agents and teams — a single Go binary with a web UI, git versioning, full-text and vector search, and access via REST, MCP, NFS, S3, WebDAV, and FUSE. Obsidian is a desktop app. KiwiFS is a server with a web UI, agent APIs, git audit trail, and server-side search over the same markdown files. Those tools store content in proprietary databases. KiwiFS stores plain markdown. `cat page.md` and `git clone` always work. No. Git runs under the hood; the API and web UI hide it. Git is the audit trail, not the primary interface. KiwiFS is in active development (currently v0.19). Core features are stable in internal production use. Public APIs may evolve before v1.0. ## Installation A single static Go binary. SQLite is embedded. The web UI is embedded via `go:embed`. Runs on macOS, Linux, and Docker. ```bash theme={null} brew install kiwifs/tap/kiwifs # or curl -fsSL https://raw.githubusercontent.com/kiwifs/kiwifs/main/install.sh | sh # or go install github.com/kiwifs/kiwifs@latest ``` Derived state — not your content. Safe to delete and rebuild with `kiwifs reindex`. ```text theme={null} .kiwi/ ├── config.toml ├── playbook.md ├── state/search.db ├── comments/ └── templates/ ``` ## Agents Three ways: 1. **Filesystem** — NFS or FUSE mount: `cat`, `echo`, `grep` 2. **REST** — `PUT /api/kiwi/file` 3. **MCP** — `kiwifs mcp --root ~/knowledge` (60+ tools) Yes. `kiwifs mcp --root ~/knowledge` runs in-process against the directory. No HTTP needed. ```json theme={null} { "mcpServers": { "kiwifs": { "command": "kiwifs", "args": ["mcp", "--root", "/path/to/knowledge"] } } } ``` See [MCP](/concepts/mcp). Send `X-Actor` and `X-Provenance` on writes. KiwiFS injects `derived-from` into frontmatter automatically. ## Search | Tier | Engine | | ---- | ------------------------------------- | | 1 | `grep` (zero deps) | | 2 | `sqlite` (FTS5, BM25 — default) | | 3 | Vector (`[search.vector]` — optional) | Use Ollama as the embedder and `sqlite-vec` as the store for fully local semantic search. ```bash theme={null} kiwifs reindex --root ./knowledge ``` ## Protocols | Protocol | Best for | | -------- | ---------------------------------- | | NFS | Docker / Kubernetes mounts | | S3 | Backup tools, pipelines | | WebDAV | Desktop sync, mapped drives | | FUSE | Developer workstation remote mount | See [Alternate protocols](/guides/alternate-protocols). Yes. Every write gets git commit, index update, and SSE broadcast regardless of entry protocol. ## Data Atomic writes (temp → fsync → rename). Git reflog aids recovery. * `git push` to any remote * `kiwifs backup` / `[backup]` in config * `kiwifs restore --from ... --to ...` * `rsync` the knowledge root ```bash theme={null} kiwifs import --from obsidian --path ~/vault --root ./knowledge kiwifs import --from notion --api-key $KEY --database-id $ID --root ./knowledge ``` ## License BSL 1.1 — self-host and embed freely. You cannot offer KiwiFS itself as a commercial hosted service. Each release converts to Apache 2.0 after four years. See [CONTRIBUTING.md](https://github.com/kiwifs/docs/blob/main/CONTRIBUTING.md). # POSIX and filesystem semantics Source: https://docs.kiwifs.com/guides/posix POSIX compliance by access path — NFS, FUSE, atomic writes, and concurrency. KiwiFS stores **real files** on disk. POSIX behavior depends on how you access them. ## Compliance by access path | Access path | POSIX level | Notes | | ----------------- | ----------- | ------------------------------------- | | Direct filesystem | Full | Real files, atomic writes, mmap works | | NFS mount | Near-full | Symlinks, open-unlink, stable handles | | FUSE mount | Near-full | Remote client; no kernel mmap | | WebDAV | Partial | MOVE/COPY/MKCOL; buffered writes | | REST / S3 / MCP | N/A | HTTP or tool semantics | ## What works on NFS and FUSE | Semantic | NFS | FUSE | | ------------------------------------ | --- | ---- | | Atomic writes (tmp → fsync → rename) | Yes | Yes | | `rename(2)` | Yes | Yes | | Symlinks | Yes | Yes | | Open-then-delete (POSIX unlink) | Yes | — | | `fsync` | Yes | Yes | | Directory rename | Yes | Yes | | `readdir` hides `.git`, `.kiwi` | Yes | Yes | | 64 MB max file size (`EFBIG`) | Yes | Yes | ## Concurrency and durability `If-Match` / ETag on REST writes. Returns `409` on conflict. One mutex across all protocols. Concurrent writers are safely queued. `flock` on `.kiwi/server.lock`. Released automatically on process exit (including SIGKILL). A background watcher cleans stale `index.lock` every 10 seconds (60-second threshold). Also cleaned at startup. `core.autocrlf=false` and `* -text` in `.gitattributes`. ETags always match raw bytes. ## Intentionally limited * FUSE does not support kernel **mmap** (HTTP-backed I/O). * WebDAV is not a full POSIX layer. * Two KiwiFS servers must not share one knowledge root — the lock prevents split-brain. ## Symlinks Symlinks work on NFS (real `os.Symlink`) and FUSE/REST (`Content-Type: application/x-symlink`). Targets escaping the knowledge root are rejected. ```bash theme={null} curl 'http://localhost:3333/api/kiwi/readlink?path=link.md' ``` ## Related documentation NFS, S3, WebDAV, FUSE setup. Storage philosophy. # Init templates Source: https://docs.kiwifs.com/guides/templates Starter templates for new knowledge bases — knowledge, wiki, runbook, research, tasks, and blank. `kiwifs init` scaffolds a knowledge directory from a built-in template. Each template provides a directory layout, sample files, a `.kiwi/config.toml`, and optional agent-facing documents. ```bash theme={null} kiwifs init --template --root ``` ## Available templates | Template | Best for | Starter files | | ----------- | ------------------------------ | ------------------------------------------------------ | | `knowledge` | LLM-maintained knowledge bases | SCHEMA.md, playbook, episodes, index, log, sample page | | `wiki` | Team wikis | Sample pages, index | | `runbook` | Ops and incident response | Runbook starters, checklists | | `research` | Research notes and literature | Note structure, bibliography | | `tasks` | Task tracking with workflows | Workflow schema, Kanban-ready frontmatter | | `blank` | Empty starting point | `.kiwi/config.toml` only | In the web UI and cloud dashboard, templates are also available when creating a new workspace or space. ## knowledge (default) The `knowledge` template is designed for AI agents that maintain a structured knowledge base. It includes everything an agent needs to onboard itself. ### Directory structure ``` knowledge/ ├── SCHEMA.md # Directory layout, frontmatter fields, naming conventions ├── index.md # Auto-maintained table of contents ├── log.md # Append-only change log ├── pages/ │ └── getting-started.md # Sample durable page ├── episodes/ │ └── example-episode.md # Sample episodic memory entry └── .kiwi/ ├── config.toml # Server configuration ├── playbook.md # MCP tool sequences for each operation └── rules.md # Agent harness rules (Cursor, Claude, etc.) ``` ### Key files Describes the knowledge base layout, frontmatter field tables, and naming conventions. Agents read this (via `kiwi_context` or `kiwi://schema`) to understand where to write and what metadata to set. Step-by-step MCP sequences for standard operations: ingest, query, deep retrieval (peek, graph\_walk, section), memory consolidation, lint loop, canvas generation, and workflow management. Includes a cost table of tools to help agents minimize unnecessary calls. Harness-specific behavior rules. Export for your agent tool: ```bash theme={null} kiwifs rules export --format cursor kiwifs rules export --format claude kiwifs rules export --format agents ``` Auto-maintained table of contents that agents update after adding pages. Seeds with a link to `pages/getting-started`. Append-only chronological record. Agents append a line after each operation for audit trail and observability. Directory for episodic memory. Agents write observations here as short-lived notes with `memory_kind: episodic` frontmatter. Periodically consolidate into durable pages using `kiwi_memory_report`. See [Episodic memory](/concepts/episodic-memory). ## wiki A structured team wiki with decision records, onboarding guides, and process documentation. ### Directory structure ``` wiki/ ├── SCHEMA.md ├── index.md ├── welcome.md ├── architecture.md ├── how-we-work.md ├── decisions/ │ ├── index.md │ └── adr-001-example.md ├── onboarding/ │ └── index.md ├── processes/ │ ├── index.md │ ├── deployment.md │ ├── dev-setup.md │ └── incident-response.md ├── reference/ │ ├── index.md │ ├── faq.md │ └── glossary.md └── .kiwi/ ├── config.toml ├── playbook.md ├── rules.md └── templates/ ├── decision.md ├── meeting-notes.md ├── onboarding.md ├── product-spec.md └── sop.md ``` The wiki template includes slash-command templates in `.kiwi/templates/` for common page types: architectural decision records, meeting notes, onboarding checklists, product specs, and standard operating procedures. These appear in the web UI's "new page" menu. ## runbook Operations-focused template with incident response procedures, postmortem templates, and escalation paths. ### Directory structure ``` runbook/ ├── SCHEMA.md ├── index.md ├── procedures/ │ ├── deploy-rollback.md │ ├── rotate-secrets.md │ └── scale-up.md ├── incidents/ │ └── template.md ├── postmortems/ │ └── template.md └── .kiwi/ ├── config.toml ├── playbook.md └── rules.md ``` Each procedure file includes step-by-step checklists with `severity` and `on_call` frontmatter fields. ## research Research template with structured note-taking, experiment tracking, and literature review pages. ### Directory structure ``` research/ ├── SCHEMA.md ├── index.md ├── experiments/ │ └── exp-001-baseline.md ├── literature/ │ └── example-paper.md ├── notes/ │ └── synthesis-template.md └── .kiwi/ ├── config.toml ├── playbook.md └── rules.md ``` Experiment files include `hypothesis`, `status`, and `result` frontmatter fields. Literature files track `authors`, `year`, and `doi`. ## tasks Task tracking template with a pre-configured workflow, JSON Schema validation, and Kanban-ready frontmatter. ### Directory structure ``` tasks/ ├── SCHEMA.md ├── WORKFLOW.md ├── index.md ├── tasks/ │ ├── example-task.md │ └── blocked-example.md └── .kiwi/ ├── config.toml ├── playbook.md ├── rules.md └── schemas/ └── task.json ``` Each task file uses structured frontmatter validated by `.kiwi/schemas/task.json`: ```yaml theme={null} --- title: "Implement auth flow" status: "in_progress" assignee: "agent:eng-bot" priority: "high" workflow: "default" state: "in_progress" --- ``` The `status` field drives Kanban board grouping. The `workflow` and `state` fields enable state-machine transitions via [Workflows](/concepts/workflows). The `task.json` schema in `.kiwi/schemas/` validates that required fields (`title`, `status`) are present and that `status` is one of the allowed values. Enable enforcement with: ```toml .kiwi/config.toml theme={null} [schema] enforce = true ``` See [Schemas](/concepts/schemas). With the workflow configured, the web UI renders tasks as a Kanban board grouped by state. Use the API to advance tasks programmatically: ```bash theme={null} curl -X POST 'http://localhost:3333/api/kiwi/workflow/advance' \ -H 'Content-Type: application/json' \ -d '{"path":"tasks/example-task.md","target_state":"done","actor":"agent:bot"}' ``` ## blank Minimal template — creates only `.kiwi/config.toml`. Use this when you want to start from scratch or bring your own directory structure. ## Listing templates via API ```bash theme={null} # Self-hosted curl http://localhost:3333/api/init-templates # Cloud curl https://api.kiwifs.com/api/workspaces/init-templates \ -H "Authorization: Bearer $KIWI_API_KEY" ``` Returns an array of template objects with `id`, `name`, and `description`. ## Related How agents onboard using SCHEMA.md and playbook.md. The episode/consolidation pattern used by the knowledge template. Full .kiwi/config.toml reference. Install and run KiwiFS in 60 seconds. # Web UI Source: https://docs.kiwifs.com/guides/web-ui Browse, edit, search, and visualize your knowledge base in the embedded KiwiFS web UI. KiwiFS ships a full **web UI** embedded in the Go binary. Open `http://localhost:3333` after `kiwifs serve` — no separate install. ## Features Expand, collapse, drag-drop, and context menu on markdown files. Source-preserving BlockNote editor. Keeps on-disk formatting stable. **Cmd+K** opens global BM25 search. Semantic results appear when vector search is configured. Force-directed wiki-link network. Saved DQL queries with table or board layouts. Interactive Flow diagrams from `.canvas.json` files. Excalidraw sketches in `.excalidraw.md` files. Boards driven by workflow state machines. Inline annotations on specific lines. Saved import connections with auto-sync. ## Layout The UI uses a three-pane layout: | Pane | Contents | | ----------- | -------------------------------------------- | | **Sidebar** | File tree, space switcher (multi-space mode) | | **Main** | Page view or editor | | **Panels** | Backlinks, ToC, comments, page view counts | ## File tree The sidebar file tree supports expand/collapse, drag-and-drop reordering, and context menus for rename, duplicate, move, and delete. You can drag files from your operating system into the tree to import them as markdown pages. ## Toolbar views Beyond the page editor, the header toolbar opens full-screen views: | Button | View | | ------------------- | ------------------------------------------------ | | **Knowledge graph** | Force-directed wiki-link network | | **Bases** | Saved DQL queries with table or board layouts | | **Canvas** | Interactive Flow editor for `.canvas.json` files | | **Whiteboard** | Excalidraw hub for `.excalidraw.md` files | | **Timeline** | Recent git-backed activity | | **Kanban** | Workflow boards grouped by state | | **Data sources** | Saved import connections and sync controls | See [Canvas](/concepts/canvas), [Whiteboard](/concepts/whiteboard), and [Import connections](/import/connections). ## Engagement metrics When analytics tracking is enabled, page view counts appear in the page header. The `kiwifs analytics` CLI command and `GET /api/kiwi/analytics` REST endpoint also report engagement data — top viewed pages and failed searches that indicate content gaps. See [Analytics API](/api/analytics). ## Page view and editor Rendered markdown with wiki links, Mermaid diagrams, and frontmatter-aware styling. Source-preserving editor (BlockNote-based). Slash commands for templates. Drag-drop images. Git versions, unified diff, and one-click restore from the versioning panel. ## Wiki navigation * `[[wiki links]]` render as clickable links; unresolved links are highlighted. * **Backlinks** panel lists pages linking to the current page. * **Graph** view visualizes the wiki-link network. ## Publishing Use **Publish** in the page header (or `POST /api/kiwi/publish`) to set `published: true` in frontmatter. Published pages are readable at `/p/{path}`. See [Publish API](/api/publish). ## Themes Built-in themes: kiwi, ocean, forest, sunset, neutral. Users can switch unless locked: ```toml .kiwi/config.toml theme={null} [ui] theme_locked = false ``` ## Mobile The UI is responsive. Reading works well on phones; editing is best on desktop. A standalone `kiwifs-ui` npm package for React embedding is on the [roadmap](https://github.com/kiwifs/kiwifs/blob/main/docs/ROADMAP.md). Until then, use [Go embed](/deploy/go-embed) or proxy `GET /` from your app. ## Related documentation Install and run in 60 seconds. Syntax and backlink behavior. Saved views and DQL. Spatial diagrams. Excalidraw sketches. # Airbyte import Source: https://docs.kiwifs.com/import/airbyte Sync data from Airbyte Docker connectors or Airbyte Cloud into markdown pages. KiwiFS can drive **Airbyte** connectors for sources easier to configure through Airbyte than native importers. Native `kiwifs import --from notion` remains the simplest path for one-shot imports. Use Airbyte when you need scheduled sync or Airbyte Cloud connections. ## Two modes Local connector containers. Phase 1 sources routed through Docker today: | Source | Connector | | --------------- | -------------------------- | | `firebase-rtdb` | Firebase Realtime Database | | `notion` | Notion | | `airtable` | Airtable | | Method | Path | Purpose | | ------ | ----------------------------------- | -------------------------- | | `POST` | `/api/kiwi/import/airbyte/spec` | Connector specification | | `POST` | `/api/kiwi/import/airbyte/check` | Validate connection config | | `POST` | `/api/kiwi/import/airbyte/discover` | List streams for a source | Hosted workspace API with discover/check/sync flows. | Method | Path | Purpose | | ------ | -------------------------------------------- | ------------------------ | | `POST` | `/api/kiwi/import/airbyte-cloud/check` | Check cloud source | | `POST` | `/api/kiwi/import/airbyte-cloud/discover` | Discover streams | | `GET` | `/api/kiwi/import/airbyte-cloud/connections` | List connections | | `POST` | `/api/kiwi/import/airbyte-cloud/sync` | Trigger sync into KiwiFS | ## Configuration ```toml .kiwi/config.toml theme={null} [import] airbyte_api_key = "${AIRBYTE_API_KEY}" airbyte_workspace_id = "${AIRBYTE_WORKSPACE_ID}" ``` Environment variables expand in TOML via `${VAR}` syntax. ## Sync status ```bash theme={null} curl 'http://localhost:3333/api/kiwi/import/sync/status' ``` Use with saved [import connections](/import/connections) when periodic sync is enabled. ## Firebase RTDB record explosion When importing Firebase Realtime Database key/value records through Airbyte, KiwiFS **explodes** each record into its own markdown page instead of collapsing the entire snapshot into one file. This makes large RTDB trees searchable and linkable in your knowledge base. ## MCP Agents can call **`kiwi_import`** with Airbyte-compatible source configuration. Available sources are listed by `GET /api/kiwi/import/sources`. ## Related documentation All 18+ import sources. REST reference. Saved connections and re-runs. # Browse, preview, and saved connections Source: https://docs.kiwifs.com/import/connections Discover tables and collections, preview import rows, and re-run imports without storing credentials on disk. Beyond a one-shot `POST /api/kiwi/import`, KiwiFS supports a small **import pipeline** for interactive tools and agents. ## Browse metadata `POST /api/kiwi/import/browse` lists **tables or collections** for a connected source. Supported `from` values today include `postgres`, `mysql`, `mongodb`, and `firestore`. The body mirrors connection fields (`dsn`, `uri`, `database`, `project`, credentials, and so on) documented in [Import and export](/api/import-export). Response shape: ```json theme={null} { "tables": [{ "name": "users" }, { "name": "orders" }] } ``` ## Preview rows `POST /api/kiwi/import/preview` streams a few records through the same importer stack and returns **path**, **frontmatter**, and a short **body\_preview** for each — without writing pages. Optional `limit` (capped) controls sample size. ## Saved connections When a connection store is enabled, the server can persist **metadata only** (no secrets): | Method | Path | Purpose | | -------- | --------------------------------------- | ---------------------------------------------------------------------------- | | `GET` | `/api/kiwi/import/connections` | List saved connections. | | `POST` | `/api/kiwi/import/connections` | Save or update metadata. | | `DELETE` | `/api/kiwi/import/connections/:id` | Remove a saved connection. | | `POST` | `/api/kiwi/import/connections/:id/run` | Run import again; **credentials must be supplied in the body** for each run. | | `POST` | `/api/kiwi/import/connections/:id/sync` | Enable, pause, or change auto-sync interval. | See [Import and export](/api/import-export#import-browse) for the same endpoints with request detail. ## Auto-sync Sources that change over time support periodic re-import: `postgres`, `mysql`, `mongodb`, `firestore`, `notion`, `airtable`, and Airbyte `firebase-rtdb`. Create a saved connection from the **Data sources** panel in the web UI or via `POST /import/connections`. Toggle auto-sync in the connection detail view, or call `POST /import/connections/:id/sync` with `{ "enabled": true, "interval": "1h" }`. Valid intervals include `5m`, `1h`, `6h`, and `24h`. Check `GET /api/kiwi/import/sync/status` or the connection badge in the web UI for `idle`, `running`, or `error` states. ### Full-sync archival When auto-sync runs, KiwiFS performs a **full sync**: pages imported from the source in previous runs that no longer appear in the latest import are tombstoned with an `_archived_at` frontmatter field rather than deleted. Re-importing a row clears the archive marker. The import response includes an `archived` count when tombstoning occurs: ```json theme={null} { "imported": 12, "skipped": 40, "archived": 3, "errors": [] } ``` Full-sync archival applies to syncable live sources without a row `limit`. One-shot CLI imports with `--limit` do not tombstone missing pages. ## File ingestion `POST /api/kiwi/ingest` converts supported office and document formats into markdown pages via the MarkItDown pipeline. See [Import and export](/api/import-export#ingest-from-files). ## Related documentation Docker and Cloud connectors. **Data sources** panel and import wizard. # Database imports Source: https://docs.kiwifs.com/import/databases Import data from PostgreSQL, MySQL, SQLite, MongoDB, Firestore, DynamoDB, Redis, and Elasticsearch into KiwiFS. KiwiFS can import rows from relational and document databases, converting each record into a markdown page with structured frontmatter. ## PostgreSQL ```bash theme={null} kiwifs import --from postgres \ --dsn "postgres://user:pass@localhost:5432/mydb" \ --table users \ --prefix people/ \ --root ./knowledge ``` ### Options | Flag | Description | | ----------- | ------------------------------------------- | | `--dsn` | PostgreSQL connection string (required) | | `--table` | Table name | | `--query` | Custom SQL query (overrides `--table`) | | `--columns` | Comma-separated fields to include | | `--prefix` | Path prefix in KiwiFS (default: table name) | | `--limit` | Max rows to import | | `--dry-run` | Preview without writing | ```bash theme={null} kiwifs import --from postgres \ --dsn "postgres://user:pass@localhost/mydb" \ --query "SELECT id, name, email FROM users WHERE active = true" \ --prefix active-users/ ``` ## MySQL ```bash theme={null} kiwifs import --from mysql \ --dsn "user:pass@tcp(localhost:3306)/mydb" \ --table articles \ --prefix articles/ ``` Same flags as PostgreSQL. The DSN format follows Go's `mysql` driver convention. ## SQLite ```bash theme={null} kiwifs import --from sqlite \ --db ./data.db \ --table entries \ --prefix entries/ ``` | Flag | Description | | --------- | --------------------------------------- | | `--db` | Path to SQLite database file (required) | | `--table` | Table name | | `--query` | Custom SQL query (overrides `--table`) | ## MongoDB ```bash theme={null} kiwifs import --from mongodb \ --uri "mongodb://localhost:27017" \ --database myapp \ --collection articles \ --prefix articles/ ``` | Flag | Description | | -------------- | --------------------------------- | | `--uri` | MongoDB connection URI (required) | | `--database` | Database name (required) | | `--collection` | Collection name (required) | ## Firestore ```bash theme={null} kiwifs import --from firestore \ --project my-gcp-project \ --collection users \ --prefix users/ ``` | Flag | Description | | -------------- | ------------------------------------ | | `--project` | GCP project ID (required) | | `--collection` | Firestore collection name (required) | Firestore requires Google Cloud credentials. Set `GOOGLE_APPLICATION_CREDENTIALS` or run within a GCP environment. ## DynamoDB ```bash theme={null} kiwifs import --from dynamodb \ --table my-table \ --region us-east-1 \ --prefix dynamo/ ``` | Flag | Description | | ---------- | ------------------------------ | | `--table` | DynamoDB table name (required) | | `--region` | AWS region (required) | | `--prefix` | Path prefix in KiwiFS | | `--limit` | Max items to import | Uses your default AWS credentials (`~/.aws/credentials`, `AWS_PROFILE`, or IAM role). ## Redis ```bash theme={null} kiwifs import --from redis \ --addr "localhost:6379" \ --pattern "article:*" \ --prefix redis/ ``` | Flag | Description | | ------------ | ----------------------------------------- | | `--addr` | Redis address (default: `localhost:6379`) | | `--password` | Redis password | | `--redis-db` | Redis database number (default: `0`) | | `--pattern` | Key glob pattern to import (default: `*`) | | `--prefix` | Path prefix in KiwiFS | Each matching key becomes a markdown page. Hash keys become frontmatter fields; string values become the page body. ## Elasticsearch ```bash theme={null} kiwifs import --from elasticsearch \ --url "http://localhost:9200" \ --table my-index \ --prefix search/ ``` | Flag | Description | | ---------- | ---------------------------------------------------------- | | `--url` | Elasticsearch URL (required) | | `--table` | Index name (required) | | `--query` | Elasticsearch query JSON (optional, defaults to match-all) | | `--prefix` | Path prefix in KiwiFS | | `--limit` | Max documents to import | ## MCP usage All database imports are also available via the `kiwi_import` MCP tool: ```json theme={null} { "tool": "kiwi_import", "arguments": { "from": "postgres", "dsn": "postgres://user:pass@localhost/mydb", "table": "users", "prefix": "people/", "dry_run": true } } ``` Use `dry_run: true` to preview what would be imported before writing any files. ## Output format Each imported record becomes a markdown file: ```markdown theme={null} --- _imported_at: "2026-04-25T18:46:15Z" _source: users _source_id: pg:users:42 name: Jane Doe email: jane@example.com role: admin --- # Jane Doe > Auto-imported from users (row pg:users:42) ``` Re-importing the same data is idempotent — KiwiFS compares `_source_id` and field values, skipping unchanged records. # File imports Source: https://docs.kiwifs.com/import/files Import data from CSV, JSON, JSONL, YAML, and Excel files into KiwiFS. KiwiFS can import structured data from local files, converting each record into a markdown page with frontmatter. ## CSV ```bash theme={null} kiwifs import --from csv \ --file data.csv \ --prefix people/ \ --root ./knowledge ``` | Flag | Description | | ------------- | --------------------------------- | | `--file` | Path to CSV file (required) | | `--columns` | Comma-separated fields to include | | `--id-column` | Column to use as filename | | `--prefix` | Path prefix in KiwiFS | | `--limit` | Max records to import | | `--dry-run` | Preview without writing | The first row of the CSV is treated as column headers. Each subsequent row becomes a markdown file. ### Example Given a CSV: ```csv theme={null} name,email,department Jane Doe,jane@example.com,Engineering Bob Smith,bob@example.com,Product ``` After import: ```markdown theme={null} --- _imported_at: "2026-04-25T18:46:15Z" _source: data.csv name: Jane Doe email: jane@example.com department: Engineering --- # Jane Doe > Auto-imported from data.csv ``` ## JSON ```bash theme={null} kiwifs import --from json \ --file data.json \ --prefix items/ ``` Expects a JSON file containing an array of objects: ```json theme={null} [ {"name": "Item A", "status": "active"}, {"name": "Item B", "status": "draft"} ] ``` ## JSONL ```bash theme={null} kiwifs import --from jsonl \ --file data.jsonl \ --prefix items/ ``` Each line is a JSON object: ```jsonl theme={null} {"name": "Item A", "status": "active"} {"name": "Item B", "status": "draft"} ``` JSONL is ideal for large datasets — KiwiFS streams lines without loading the entire file into memory. ## YAML ```bash theme={null} kiwifs import --from yaml \ --file data.yaml \ --prefix items/ ``` Expects a YAML file containing a list of mappings: ```yaml theme={null} - name: Item A status: active - name: Item B status: draft ``` Each mapping becomes a markdown page with its keys as frontmatter fields. ## Excel ```bash theme={null} kiwifs import --from excel \ --file data.xlsx \ --prefix records/ \ --sheet "Sheet1" ``` | Flag | Description | | ------------- | -------------------------------------------------- | | `--file` | Path to `.xlsx` file (required) | | `--sheet` | Sheet name to import (defaults to the first sheet) | | `--columns` | Comma-separated columns to include | | `--id-column` | Column to use as filename | | `--prefix` | Path prefix in KiwiFS | | `--limit` | Max rows to import | | `--dry-run` | Preview without writing | The first row is treated as column headers. ## MCP usage File imports are available via the `kiwi_import` MCP tool: ```json theme={null} { "tool": "kiwi_import", "arguments": { "from": "csv", "file": "/path/to/data.csv", "prefix": "people/" } } ``` MCP file imports require local mode (`kiwifs mcp --root`). The file path must be accessible to the KiwiFS process. ## Column filtering Use `--columns` to import only specific fields: ```bash theme={null} kiwifs import --from csv --file data.csv --columns "name,email,status" --prefix people/ ``` Fields not listed are excluded from the frontmatter. # Data Import Source: https://docs.kiwifs.com/import/overview Import data from 18 sources into your KiwiFS knowledge base. KiwiFS can import data from databases, files, and SaaS platforms — converting each row/record into a markdown page with structured frontmatter. ```bash theme={null} kiwifs import --from [options] --root ./knowledge ``` ## Supported sources `--from postgres` `--from mysql` `--from sqlite` `--from mongodb` `--from csv` `--from json` `--from yaml` `--from excel` `--from notion` `--from airtable` `--from gsheets` `--from firestore` Plus: JSONL, Obsidian vaults, Confluence exports, DynamoDB, Redis, Elasticsearch. ## Key features * **Idempotent** — re-importing the same data skips unchanged records * **Column filtering** — `--columns "name,email,status"` to include only specific fields * **Dry run** — `--dry-run` to preview what would be imported * **Limit** — `--limit 100` to import a subset * **Custom queries** — `--query "SELECT * FROM users WHERE active = true"` For HTTP APIs that **browse** schemas, **preview** rows, and manage **saved connections**, see [Browse, preview, and saved connections](/import/connections) and [Import and export](/api/import-export#import-browse). For **Airbyte** Docker and Cloud sync, see [Airbyte import](/import/airbyte). ## How it works Each record becomes a markdown file with frontmatter: ```markdown theme={null} --- _imported_at: "2026-04-25T18:46:15Z" _source: users _source_id: pg:users:42 name: Jane Doe email: jane@example.com role: admin --- # Jane Doe > Auto-imported from users (row pg:users:42) ``` The `_source_id` field enables idempotent re-imports — KiwiFS compares field values and skips records that haven't changed. # SaaS imports Source: https://docs.kiwifs.com/import/saas Import data from Notion, Airtable, Google Sheets, Obsidian, and Confluence into KiwiFS. KiwiFS can import data from third-party SaaS platforms, converting records into markdown pages. ## Notion Import pages from a Notion database. ```bash theme={null} kiwifs import --from notion \ --prefix knowledge/ \ --root ./knowledge ``` | Flag | Description | | --------------- | ------------------------------------- | | `--database-id` | Notion database ID (required for MCP) | | `--prefix` | Path prefix in KiwiFS | | `--limit` | Max records to import | | `--dry-run` | Preview without writing | ### Setup 1. Create a [Notion integration](https://www.notion.so/my-integrations) and copy the API key. 2. Share your database with the integration. 3. Set the `NOTION_API_KEY` environment variable. ```bash theme={null} export NOTION_API_KEY="ntn_..." kiwifs import --from notion --root ./knowledge ``` ### MCP usage ```json theme={null} { "tool": "kiwi_import", "arguments": { "from": "notion", "database_id": "abc123...", "prefix": "notion-pages/" } } ``` ## Airtable Import records from an Airtable base. ```bash theme={null} kiwifs import --from airtable \ --prefix records/ \ --root ./knowledge ``` | Flag | Description | | ------------ | ------------------------------------ | | `--base-id` | Airtable base ID (required for MCP) | | `--table-id` | Airtable table ID (required for MCP) | | `--prefix` | Path prefix in KiwiFS | | `--limit` | Max records to import | ### Setup 1. Create a [personal access token](https://airtable.com/create/tokens) in Airtable. 2. Set the `AIRTABLE_API_KEY` environment variable. ```bash theme={null} export AIRTABLE_API_KEY="pat..." kiwifs import --from airtable --root ./knowledge ``` ### MCP usage ```json theme={null} { "tool": "kiwi_import", "arguments": { "from": "airtable", "base_id": "app...", "table_id": "tbl...", "prefix": "airtable/" } } ``` ## Google Sheets Import rows from a Google Sheets spreadsheet. ```bash theme={null} kiwifs import --from gsheets \ --spreadsheet-id "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms" \ --sheet "Sheet1" \ --prefix gsheets/ \ --root ./knowledge ``` | Flag | Description | | ------------------ | ----------------------------------------------------------------------------- | | `--spreadsheet-id` | Google Sheets spreadsheet ID (required) | | `--sheet` | Sheet name (defaults to first sheet) | | `--credentials` | Path to Google service account JSON (or set `GOOGLE_APPLICATION_CREDENTIALS`) | | `--id-column` | Column to use as filename | | `--prefix` | Path prefix in KiwiFS | The first row is treated as column headers. ## Obsidian Import an Obsidian vault into KiwiFS, preserving wiki-links and frontmatter. ```bash theme={null} kiwifs import --from obsidian \ --path ~/my-vault \ --prefix vault/ \ --root ./knowledge ``` | Flag | Description | | ---------- | ----------------------------------------------- | | `--path` | Path to the Obsidian vault directory (required) | | `--prefix` | Path prefix in KiwiFS | Obsidian's `[[wiki-link]]` syntax is preserved — KiwiFS uses the same link format natively. ## Confluence Import pages from a Confluence space. ```bash theme={null} kiwifs import --from confluence \ --url "https://your-domain.atlassian.net" \ --prefix confluence/ \ --root ./knowledge ``` | Flag | Description | | ---------- | ------------------------------ | | `--url` | Confluence base URL (required) | | `--prefix` | Path prefix in KiwiFS | | `--limit` | Max pages to import | Confluence import uses the Confluence REST API. Authenticate via API token set as environment variable or passed through the Airbyte connector for richer sync. See [Airbyte import](/import/airbyte). ## Re-import behavior All SaaS imports are idempotent. KiwiFS uses `_source_id` in frontmatter to track which records have been imported. Re-running an import: * **Skips** records that haven't changed * **Updates** records with new field values * **Creates** new records that weren't imported before Use `--dry-run` to preview changes before writing: ```bash theme={null} kiwifs import --from notion --dry-run ``` # Introduction Source: https://docs.kiwifs.com/index KiwiFS is a markdown filesystem for agents and teams. Searchable. Structured. Versioned. One binary, zero config. KiwiFS is a **markdown filesystem for agents and teams** — one Go binary that turns markdown on disk into a searchable, queryable, versioned store accessible via REST, MCP, NFS, S3, WebDAV, and FUSE. ## Why KiwiFS? Markdown is the lingua franca of agents and developers — but raw `.md` files are just files. No search, no versioning, no structure. KiwiFS layers database-like guarantees on top of plain markdown: * **Files are the truth** — every page is a plain markdown file with YAML frontmatter. No proprietary format, no lock-in. * **Agents already know the API** — `cat`, `grep`, `echo` work out of the box. No SDK needed. * **Every write is a git commit** — full history, blame, diff, and rollback for free. * **Full-text + vector search** — BM25 via SQLite FTS5 out of the box, with pluggable vector embeddings. * **Query language (DQL)** — SQL-like queries over frontmatter metadata. * **Web UI included** — wiki links, knowledge graph, Mermaid diagrams, markdown editing, Bases, canvas, comments, and dark mode. * **Single binary, zero config** — download and run. No Node, no Docker required (Docker optional). ## How it works flowchart TD subgraph Protocols REST\["REST API :3333"] NFS\["NFS :2049"] S3\["S3 :3334"] WebDAV\["WebDAV :3335"] FUSE\["FUSE mount"] MCP\["MCP stdio/HTTP"] end REST & NFS & S3 & WebDAV & FUSE & MCP --> Storage subgraph KiwiFS\["KiwiFS Server"] Storage\["Storage Layer"] --> Git\["Git Commit"] Git --> Index\["Search Index"] Index --> SSE\["SSE Broadcast"] end All protocols flow through the same storage layer. Every write — regardless of how it enters — gets a git commit, a search index update, and an SSE broadcast. ## For AI agents KiwiFS publishes machine-readable documentation for LLMs and agent frameworks: * **[llms.txt](/llms.txt)** — concise overview with key endpoints and MCP tool names * **[llms-full.txt](/llms-full.txt)** — complete API + MCP + CLI reference in a single file Point your agent at `llms-full.txt` to give it everything it needs to read, write, search, and manage a KiwiFS knowledge base. ## Next steps Install and run KiwiFS in 60 seconds. Configure search, auth, versioning, and more. How AI agents read and write markdown. Connect Claude, Cursor, and other AI tools. Stage agent edits in git-backed drafts before merge. Saved DQL queries with table and board layouts. Browse, edit, and visualize your knowledge base. Agent workflows, DQL, import, and deployment recipes. Common questions about KiwiFS. # Quickstart Source: https://docs.kiwifs.com/quickstart Install and run KiwiFS in 60 seconds. ## 1. Install ```bash theme={null} curl -fsSL https://raw.githubusercontent.com/kiwifs/kiwifs/main/install.sh | sh ``` ```bash theme={null} go install github.com/kiwifs/kiwifs@latest ``` ```bash theme={null} docker run -p 3333:3333 -v ./knowledge:/data ameliaanhlam/kiwifs ``` The Docker image runs `kiwifs serve` with default settings. Skip to step 4 — init and serve are handled automatically. ## 2. Initialize a knowledge base ```bash theme={null} kiwifs init --template knowledge --root ./knowledge ``` This creates a directory with starter files, a schema, and an agent playbook. Templates available: `knowledge` (default), `wiki`, `runbook`, `research`, `tasks`, `blank`. See [Init templates](/guides/templates) for details on each. ## 3. Start the server ```bash theme={null} kiwifs serve --root ./knowledge ``` KiwiFS is now running at `http://localhost:3333`. Open it in your browser to see the web UI. ## 4. Write from your agent ```bash Via REST theme={null} curl -X PUT 'http://localhost:3333/api/kiwi/file?path=concepts/auth.md' \ -H "X-Actor: my-agent" \ -d "# Authentication OAuth2 + JWT based authentication system." ``` ```bash Via filesystem theme={null} echo "# Authentication" > ./knowledge/concepts/auth.md ``` ```bash Via MCP theme={null} kiwifs mcp --root ./knowledge ``` ## 5. Search ```bash theme={null} curl 'http://localhost:3333/api/kiwi/search?q=authentication' ``` ## Connect an AI agent Add KiwiFS as an MCP server in Claude Desktop or Cursor: ```json theme={null} { "mcpServers": { "kiwifs": { "command": "kiwifs", "args": ["mcp", "--root", "/path/to/knowledge"] } } } ``` Your agent now has access to **60+ MCP tools** for reading, writing, searching, querying, drafts, and more. See [MCP](/concepts/mcp) for the full tool reference. ## Next steps Auth, search, versioning, vector embeddings, and more. Filesystem, REST, and MCP access patterns. Query frontmatter metadata with a SQL-like language. Production deployment with Docker Compose. Tree, editor, graph, Bases, and search. Episodes, consolidation, and memory reports.