--- title: "kmem" type: reference tags: [signpost, root, reference] proposed_by: tok_bc09b340db294c709763789154d8113d model: kmem-docs-publisher extracted_at: "2026-10-08T22:24:36.060Z" --- # kmem A hosted team knowledge base for people and AI agents. kmem is short for knowledge memory. It stores plain Markdown, indexes it into a searchable graph, and serves it to assistants over MCP and over Quick Connect. People write in a simple Markdown editor. Agents read and write the same corpus. By default every agent change waits in Review for a person. An owner can let chosen connections write directly. This repository is the product. It is private, and the kmem team operates every deployment. **Using kmem, not building it?** Read the user guide, from [`docs/user-guide/010-welcome.md`](./docs/user-guide/010-welcome.md). The app serves the same pages at `/help`. ## What makes it different 1. **Plain Markdown is the ground truth.** If D1, Vectorize and the app all vanish, the R2 bucket plus a git clone still hold everything, readable in any editor. An export stays true, byte for byte. 2. **Retrieval is the product.** One `recall` returns a ranked, deduplicated ContextPack inside a token budget, with provenance on every claim and an explicit list of what the corpus does not know. It does not return raw notes. 3. **Agents propose. People decide.** An agent write lands in the review queue. A person accepts it, rejects it, or keeps both versions. That is what makes a shared record trustworthy. An org that trusts a connection can turn on **Agent writes**, and even then a deletion or a stale edit waits. 4. **One write path.** Every mutation goes through `CommitService`, whatever door it came in by. ## The loop Three steps, and the app explains each one as it happens. 1. **Create an organization.** Under 60 seconds. The organization gets its own subdomain at `https://{org}.kmem.app`, isolated from every other tenant. The form asks for two settings, both off by default: **Agent writes** (may a connection change docs without Review) and **Verification** (do answers rank checked facts first and show who wrote each fact). 2. **Connect an agent, or import your docs.** ChatGPT, Claude and the Gemini web app sign in to kmem (hosted OAuth). Every other client takes a connection token, and Connect prints its setup with the token filled in. Quick Connect gives an agent that cannot speak MCP a private URL. Import takes Markdown files, a folder, a zip or an Obsidian vault, up to 25 MiB and 300 files, and reports the outcome per file: added, skipped, sent to review or failed. Import can also turn the memory that another AI keeps about you or your team into proposed docs, which you review before kmem adds them. 3. **Read the docs, decide in Review, and Ask.** A conflict becomes a proposal, and a proposal waits for a person. Ask answers a question from the docs, with the docs it used. ## The doors **MCP** is the primary agent door. Streamable HTTP, seven tools, a bearer token in the header. A client that signs in gets the token through hosted OAuth; a coding agent takes one from Connect: ```sh claude mcp add --transport http --scope user kmem https://{org}.kmem.app/mcp \ --header "Authorization: Bearer sp_..." ``` **Quick Connect** is the secondary door, for a client with no MCP support. A person creates a connection and gives its URL to the agent. The URL serves a credential-bearing `AGENTS.md` that teaches the verbs: ``` https://{org}.kmem.app/s/{sp_token} ``` > **The URL is the credential. Treat it like a password and prefer a short expiry.** > The link grants whatever scope it was minted with to anybody who holds it. Revocation takes effect > within one request. The link a person pastes has no file extension. A URL that ends `.md` gets routed to a chat client's code interpreter, which has no network, so the request never reaches the origin at all. **The web app** is the door for people. Its navigation has four areas: Docs (with each doc's reader, editor, history and Share dialog), Ask, Review and Connect. The org menu opens Org settings and Members and groups for owners and admins; the account menu opens Account, Export docs and the help. [`docs/API.md`](./docs/API.md) is the endpoint reference. ## MCP tool footprint kmem exposes seven tools and no more. Every tool definition a server publishes is spent from the client's context window before the conversation begins, so the total is capped and measured. **Total tool-definition footprint: 1,827 tokens** (hard cap 3,500). `pnpm mcp:footprint:check` re-measures the definitions. It fails when that line disagrees with them, and when the number exceeds the cap. A running deployment publishes the same number at `/health`, so you can check it without trusting this file. | Tool | Scope | Annotation | On Quick Connect | | --- | --- | --- | --- | | `recall` | read | `readOnlyHint`, `idempotentHint` | yes | | `read` | read | `readOnlyHint`, `idempotentHint` | yes | | `browse` | read | `readOnlyHint`, `idempotentHint` | yes | | `remember` | write | not idempotent | yes | | `edit` | write | `destructiveHint` | yes | | `verify` | write | `idempotentHint` | yes | | `link` | write | `idempotentHint` | no, MCP only | ## The repository | Path | What lives there | | -------------------- | ----------------------------------------------------------------- | | `apps/worker` | Hono app, MCP server, Quick Connect, REST, auth, DOs | | `apps/web` | React SPA. The app a person signs in to, and the landing page | | `packages/core` | Parser, chunker, fusion, scoring, budget packer. Zero Cloudflare | | `packages/db` | Drizzle schema and D1 migrations | | `packages/cli` | `npx kmem` init, ingest, export, import. Operator tooling | | `packages/agent-kit` | Claude Code plugin, Agent Skill, hooks, stdio bridge | | `packages/mcp` | The stdio bridge, for a client that speaks stdio only | | `eval/` | The retrieval eval set and its runner | | `e2e/` | Playwright | | `docs/` | See [`docs/README.md`](./docs/README.md) — it is the index | | `design/` | The design contract, the design system, one spec per screen | | `brand/` | The bundled fonts, their licences, and the brand token file | | `wrangler.jsonc` | Every binding, in one file | Two rules about this layout are load-bearing. **`packages/core` has zero Cloudflare imports.** The parser, the chunker, fusion, freshness decay, near-duplicate collapse and the budget packer are pure functions over plain data. Biome enforces it: an import of `cloudflare:*`, `@cloudflare/*`, `drizzle-orm` or `hono` from `packages/core/src` is a lint error. When core needs a capability it cannot compute, define a narrow function type in `types.ts` and pass an implementation in from `apps/worker`. **Every mutation goes through `CommitService.commit(ctx, change)`.** The editor, MCP, a connection document, a git pull and a folder ingest all use one code path. No R2 or D1 write happens anywhere else, and `apps/worker/test/invariants/single-write-path.test.ts` proves it. A `bucket.put` or a `db.insert` outside `CommitService` is the bug. ## How to work here Node 24 and pnpm 10. Nothing else: no Cloudflare account and no API key. ```sh pnpm install pnpm test:core # pure logic, plain Node. Use this while iterating pnpm test:worker # inside workerd against real local D1, R2, KV, Queues and Durable Objects pnpm test # every project. About 17 minutes on an idle machine pnpm typecheck # tsc --build --force over every project, tests and configs included pnpm lint # biome. 8 known warnings, all in eval/. Zero errors is the bar pnpm coverage # gates: packages/core >= 95%, overall >= 85% pnpm licence-check # names the package and the path when it refuses pnpm docs:links # every relative Markdown link resolves. Runs in CI pnpm docs:manifest # docs/manifest.json and the directory describe each other. Runs in CI pnpm mcp:footprint:check # the footprint line above still matches the definitions pnpm dev # wrangler dev pnpm release # migrations, Vectorize metadata indexes, deploy. NOT `pnpm deploy` node eval/runner.mjs --gate # the retrieval gate ``` Run the project or the files you touched rather than the whole suite: ```sh npx vitest run --project web test/shell/rail.test.tsx # from apps/web npx vitest run --project worker test/auth/config.test.ts # from apps/worker ``` Cloudflare bindings are never mocked. `@cloudflare/vitest-pool-workers` gives real local D1, R2, KV, Queues and Durable Objects. A mocked binding produces a green test and a broken deployment. Four things will confuse you if nobody says them. - `pnpm coverage` failing with `ENOENT ... coverage/.tmp` is a stale temp directory from an interrupted run. Delete `coverage/` and run it again. - `node eval/runner.mjs` prints `FAIL` and exits `0` unless you pass `--gate`. - `pnpm deploy` is a pnpm builtin. It answers `ERR_PNPM_NOTHING_TO_DEPLOY` and never reaches this repository's script. The deploy script is `pnpm release`, and it is the one that provisions the Vectorize metadata indexes. - Seven source files carry a literal NUL byte as a sentinel, so a `grep` that skips binary files returns nothing at all for them rather than an error. Use `/usr/bin/grep -a`. [`CONTRIBUTING.md`](./CONTRIBUTING.md) is the engineering guide. [`CLAUDE.md`](./CLAUDE.md) is the brief for an agent working on kmem itself. ## Deployment kmem is hosted. The kmem team provisions and operates every deployment on `kmem.app`, so a customer never deploys anything: they sign up, and the organization is there. The product answered on `sarani.app` until 2026-09-11. That host is still routed, and every request to it answers a temporary redirect to the same path on `kmem.app` — the apex, and each organization's own subdomain. An old bookmark, an old MCP endpoint and an old `/s/{token}` link all still resolve. The redirect is temporary on purpose, because `sarani.app` will belong to another project later, so update the address rather than relying on it. [`docs/DEPLOYMENT.md`](./docs/DEPLOYMENT.md) is the operator procedure — provisioning, migrations, secrets, the first owner, backup and export, rollback, disaster recovery, and how the documentation site is published. ### Moving a knowledge base between deployments ```sh export SIGNPOST_COOKIE='' npx kmem export --url https://old.kmem.app --out backup.tar.gz npx kmem import backup.tar.gz --url https://new.kmem.app --force ``` The tarball is the Markdown, byte for byte, plus a manifest of `content_sha` values that `import` checks before it sends anything. Every note goes in through the ordinary write path, so the import gets the visibility clamp, the write policy, provenance and the index queue. It is idempotent, so a second run changes nothing. Two limits, stated plainly. A note's permission level travels inside its own front matter, so a private note stays private across a round trip. A folder's level does not travel, because a folder setting lives in the deployment rather than in a file, so a note that inherited its level resolves to the destination's folder setting instead. Both commands print the count when that happens. And an export holds what the session running it may read, not what the deployment holds, because an export must not be a way around the permission model. ## Documentation [`docs/README.md`](./docs/README.md) is the index. It opens with two reading orders, one for an engineer on this repository and one for an agent working on it. Three shortcuts if you already know what you want: - [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) — how it is built, and the invariants, each naming the real failure it prevents. - [`docs/RETRIEVAL.md`](./docs/RETRIEVAL.md) — the pipeline stage by stage, scoring, the ContextPack and the eval method. - [`docs/CODEBASE.md`](./docs/CODEBASE.md) — where a thing lives, the extension points with the example to copy, and the traps. `RELAUNCH_NOTES.md` says what is broken right now. Every document is classified `public` or `internal` in [`docs/manifest.json`](./docs/manifest.json), so the documentation site is generated rather than curated. `internal` is not a secrecy marking: it means a reader who does not build kmem is not the audience. ## Licence See [`LICENSE`](./LICENSE). No GPL, AGPL or LGPL package may reach a shipped artefact. `pnpm licence-check` enforces that and prints the offending package and path when it fails. BlockNote is MPL-2.0, which is file-level copyleft: safe as a dependency, never to be forked.