● meerkat · knows
meerkat
A knowledge base served as a single static Go binary — three ways, CLI, MCP, and HTTP — so your developers and agents start from what is actually true at your org.
meerkat exposes a Markdown wiki as a CLI (mk search/show/list), an MCP server over stdio for agent harnesses, and an HTTP/OpenAPI server with bearer auth for tools like OpenWebUI. Content can be compiled into the binary at build time, or resolved at runtime from a directory or a signed archive. Either way search runs in-process with no external service and no network. It is the knows leg of the platform.
No credentials in the binary
The binary ships with no LLM credentials and is read-only over the knowledge base. The only "live" action — ingestion — shells out to your agent CLI, running under your own account.
Installation
The binary is meerkat, with a short alias mk. The repo is public and Apache-2.0, so installation works anonymously — no login or token required.
PLATFORM=darwin_arm64
mkdir -p ~/.local/bin
gh release download --repo zegit-zoo/meerkat \
-p "meerkat_*_${PLATFORM}.tar.gz" --output - \
| tar -xz -C ~/.local/bin meerkat
ln -sf meerkat ~/.local/bin/mk
meerkat versionNo tag is pinned above — gh release download with no tag argument fetches the latest release. Keep current with mk update --check then mk update. Releases are cosign-signed as a Sigstore bundle; mk update verifies the signature over the checksum file before trusting any hash, then does an atomic swap.
Core concepts
Content sources
The wiki body, the ingestion source registry, prompts, and templates are configuration, not code. The mechanism is content-source.yaml (copy from the example in the repo). The open-source build ships no content at all — every example returns nothing until you point it at your own.
content:
type: git # none | local | git | submodule | url
repo: your-org/meerkat-kb
host: github # github | gitlab — selects the gh/glab token
ref: v1.2.0 # tag or commit SHA (pin for reproducibility)
layout:
wiki: wiki # markdown pages
sources: ingestion/sources.yaml # the source registry
prompts: ingestion/prompts # per-source prompts
templates: templates # page templatesBuild time vs. runtime
Not every source type works at both ends. git and submodule are build-time only — they are resolved by make sync and baked in. local and url also resolve at runtime, so one released binary can serve different knowledge bases without a rebuild.
| Source | Build time | Runtime | Notes |
|---|---|---|---|
none | yes | yes | Embedded placeholders — the default. |
local | yes | yes | A directory on disk. Unverified. |
git | yes | — | Cloned at build; borrows a cached gh token for GitHub. |
submodule | yes | — | A git submodule in the repo. |
url | — | yes | An HTTPS .tar.gz. sha256 is required and verified before extraction. |
At startup meerkat resolves content in a fixed order, first match wins: --kb-dir (or MEERKAT_KB_DIR), then --content-source (or MEERKAT_CONTENT_SOURCE), then content-source.yaml in the user config dir, then one in the working directory, and finally the embedded build.
mk --kb-dir ~/kb list # serve a directory directly
mk --content-source ./content-source.yaml list
mk version --json | jq .kb_source # what am I actually serving?Provenance is explicit
mk version reports kb_source as embedded, disk:<path>, or url:<url>@<digest>. Only the url form is digest-verified — disk: means meerkat read whatever was on disk and checked nothing. The separate kb_commit field always describes the build-time embedded content and does not change to reflect runtime content.
OKF bundles
meerkat serves an OKF (Open Knowledge Format) knowledge bundle directly, with no conversion step — OKF is a directory of Markdown files with YAML frontmatter, which is already meerkat's storage model. meerkat implements the consumer side only: it reads bundles, it does not produce them.
A bundle's root is its content root, so point layout.wiki at it:
content:
type: local
path: /path/to/the-bundle # the bundle root itself
layout:
wiki: "." # concepts sit directly under itUse --content-source, not --kb-dir
--kb-dir always assumes the default layout (a wiki/ subdirectory) and has nowhere to carry a layout: override, so pointing it at a bundle root finds no pages and reports an empty knowledge base.
Two OKF frontmatter fields are promoted into meerkat's core: type (OKF's only required key, and a filter facet — see mk list --type) and description. The trust family — generated, verified, stale_after — is surfaced on every read path as two computed fields, trust_tier and stale. Everything else OKF defines (resource, sources, okf_version, the Attested Computation keys) is preserved verbatim under front.extra.
verified | trust_tier |
|---|---|
| absent | unverified |
present, no entry's by starts with human: | machine-confirmed |
present, at least one by starts with human: | human-reviewed |
trust_tier is advisory, not verified
The tier is asserted by whoever produced the bundle — meerkat derives it from the frontmatter but checks nothing. A producer can write verified: {by: human:anyone} freely. Read it the way you read the disk: provenance label, not as an integrity guarantee.
OKF reserves index.md and log.md for navigation, and meerkat skips them — but keyed on whether the file has frontmatter, not on the filename, so meerkat's own knowledge bases can keep using index.md as an ordinary landing page. OKF v0.2 is early and unratified, and meerkat is an independent third-party consumer, not affiliated with or endorsed by its authors. Full reference: docs/OKF.md.
Ingestion
meerkat is the planner; an agent CLI (OpenCode by default) is the executor. Pages begin as placeholders pointing at an upstream source; mk ingest renders one task per page, and mk ingest --execute spawns an agent session per page (under a wall-clock cap) to write the page, commit, and push.
mk ingest sources # list the source registry
mk ingest --source policies # plan only (writes a JSONL batch)
mk ingest --source policies --execute --max-parallel 4Search
An in-process Bleve BM25 index over the served markdown — no external service, no network, no embedding model. Built in-memory at startup; three indexed fields with boosts (title ×5, id ×3, body ×1). The body field accepts Bleve query strings.
Frontmatter filtering is post-search
Field filters like --owner and --type compose after the BM25 query — frontmatter is not indexed, so don't present owner:team-x as a working query term. Those filters exist on mk list, not on mk search.
Next
Authoritative reference, generated from the component repos. Spot something stale? Tell us.