OKF4net 0.6.0: two months, 642 commits, and a test suite bigger than the code

OKF4net 0.6.0 is out. It is the largest release of the project so far: more commits than every previous release combined, two new command-line verbs, a static-site generator, and a way to run a knowledge bundle’s computations in real containers.

If you have never heard of the project: the [Open Knowledge Format](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md) (OKF) is Google’s open format for representing knowledge as a plain directory of markdown files with YAML frontmatter. It is meant to be readable by a person with `cat` and by an AI agent alike. [OKF4net](https://github.com/jchable/okf4net) is an independent .NET implementation of OKF v0.2, written against the spec, on the base class library only. The core library, the catalog and the `okf` binary have no third-party dependencies, down to their own YAML-subset parser.

The release in numbers

The first release shipped on 22 July. Ten weeks later:


0.1.0 (22 Jul)0.5.0 (31 Jul)0.6.0 (1 Oct)
Commits (cumulative)545521,194
Projects under src/2710
Lines of C# in src/4,69913,43924,120
Lines of C# in tests2,30713,41830,512
Tests run2189122,352
NuGet packages166
Agent tools (okf_*)01113

Line counts are non-blank lines, comments included. 0.6.0 alone is 642 commits and 146 changelog entries.

The number I care about most is the test row: the suite went from 912 to 2,352 tests, and there is now more test code than product code. Most of what this release did was make existing behaviour harder to break.

What is new

okf audit and okf verify : A bundle is only useful if you know which parts of it someone has actually checked. `okf audit` answers that across the whole bundle: which concepts are stale, which have never been reviewed by a human, where provenance is missing.

okf audit bundles/acme_retail --stale --trust unverified,machine-confirmed
okf audit bundles/acme_retail --stale --trust unverified,machine-confirmed

okf verify records a dated review on a concept (§5.2 of the spec). The two compose over a pipe, so « review everything nobody has reviewed » is one line:

okf audit bundles/acme_retail --trust unverified | cut -d' ' -f1  | okf verify bundles/acme_retail --by human:ada -

A verification stamp is a dated declaration, not a proof: okf verify cannot authenticate who ‘–by’ names. The README says so, and so do I.

okf-render : a second binary that turns a bundle into a browsable static HTML site. It is separate from `okf` on purpose: `okf validate` is the small validator people run in CI, and it should not carry a JavaScript markdown renderer it never executes.

Attested computation in real containers : OKF’s §10 lets a concept declare a computation (a script or a SQL query) and an attester that checks the result. The new `OKF4net.Attestation.Containers` runs the bundle’s *actual* script and its *actual* attester in Docker, Podman or nerdctl, as an unprivileged user with every capability dropped. Nothing is reimplemented in C#. This one is in the repository but not yet published as a package, because it needs a container engine at run time.

A C# code graph in the producer. okfgen, the tool that generates a bundle from a repository, now emits one concept per namespace, type and member, with resolved call links between them. It grew from about 1,000 lines to about 31,800, more than half of them tests.

A hardened agent surface : the MCP server okf-mcp now serves a bundle read-only by default; you opt in to the write tools with ‘OKF_MCP_WRITABLE=1’. Computations run through an agent have a two-minute timeout and can be cancelled. Text that a bundle controls is neutralised before a model sees it.

## What the hardening actually looked like

One story from this cycle is worth telling. The viewer renders markdown in the browser, so it has to stop a hostile bundle from injecting script. We had two tests asserting that raw HTML was disabled. Both were green. The sanitizer had an exploitable hole anyway.

The reason is simple: the test suite runs on .NET and cannot execute JavaScript. Those tests were checking that certain strings appeared in a `.js` file. They would have passed with the sanitizer deleted.

The fix was not a better assertion. It was a separate Node harness that loads the real viewer code and feeds it hostile payloads, run in CI on every change. The same rule now applies everywhere in the repository: code the test runner cannot execute needs its own executable check, and a test that only reads source text has to say it is a smoke check.

## Breaking changes

This is a 0.x release and it breaks things. The ones most likely to affect you:

  • A bare ‘attester.resource’ or ‘computation’ path now resolves from the bundle root, not from the concept’s folder (§6.2). okf validate tells you where it found the file and what to write instead.
  • The frontmatter fence must be ‘—‘ at column 0. YAML anchors, aliases and   tags are rejected with an error that names the feature.
  • Bundle.ReadResourceText refuses any path outside the bundle root.
  • okf-mcp is read-only unless you set `OKF_MCP_WRITABLE=1`.

The changelog ➡️ https://github.com/jchable/okf4net/blob/main/CHANGELOG.md#060—2026-10-01 lists every one of them at the top of the 0.6.0 section.

Try it

dotnet add package OKF4net          # the library

dotnet tool install -g OKF4net.Mcp  # the MCP server

Prebuilt okf and okf-render binaries for Windows, Linux and macOS (x64 and arm64) are on the GitHub release page : https://github.com/jchable/okf4net/releases/tag/v0.6.0.

The documentation lives at https://jchable.github.io/okf4net.

## Want to help?

Nearly every commit so far is mine, and I would like that to change. There is a public roadmap : https://github.com/jchable/okf4net/blob/main/ROADMAP.md and a set of issues labelled ‘good first issue’ : https://github.com/jchable/okf4net/labels/good%20first%20issue.

Two areas where a second pair of eyes would help most: the container runtime has only ever been exercised on Docker, never on Podman or nerdctl, and the producer’s code graph only covers C# so far.

OKF4net agent memory you can git blame

Most answers to « give my AI agent long-term memory » hand you an opaque vector store: embeddings in a database you can’t read, can’t diff, and can’t easily redact. I wanted the opposite — memory I can open in an editor, review in a pull request, and git blame line by line. That itch is what pushed OKF4net from a single library into a small toolkit over the last few months. It just reached v0.5, and this post is about what’s new — starting with the part I’m most excited about.

If you haven’t seen it before: OKF4net (docs & project site) is an independent, zero-dependency .NET implementation of Google’s Open Knowledge Format (OKF). OKF represents knowledge as a directory of markdown files with YAML frontmatter — cross-linked like a wiki, versioned in git. No database, no proprietary format: if you can cat a file you can read it, if you can git clone a repo you can ship it.

The headline: agent memory as plain markdown

OKF4net.Agents turns a bundle into tools and context for the Microsoft Agent Framework. OkfBundleTools exposes read, browse, graph, search, write, append-log, validate and more as function tools an agent can call directly. Layer OkfContextProvider onto the same instance and the agent automatically gets relevant bundle context injected into each turn — and, when you opt in, its exchanges captured back into the bundle as long-term memory:

using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OKF4net.Agents;
var tools = new OkfBundleTools("./my_bundle");
// Memory capture is opt-in (Disabled by default) — turn it on explicitly.
var provider = new OkfContextProvider(
    tools,
    new OkfContextProviderOptions { MemoryCapture = MemoryCaptureMode.Enabled });
AIAgent agent = chatClient.AsAIAgent(new ChatClientAgentOptions
{
    ChatOptions = new ChatOptions { Tools = tools.GetTools() },
    AIContextProviders = [provider],
});
var response = await agent.RunAsync("What do we know about orders?");

Here’s what makes it different. Capture is deterministic — no extra LLM call: after each turn, the last user message and the agent’s final response are appended to a single memory concept for the day (memory/<yyyy-MM-dd>), with a matching log.md change-history entry. Those writes go through the exact same validated, lock-protected, path-safe write path any other caller uses, and the captured text is blockquote-neutralized so a payload smuggled into a message can’t fake document structure.

The payoff is that memory is just files in your bundle. You can:

  • git blame a remembered fact to see exactly when and in which exchange it entered the memory,
  • diff memory across commits and redact a line with a normal edit,
  • point a second agent at the same directory with no export step,
  • and review the whole thing in a PR like any other change.

I’ll be honest about where v1 stands: this captured memory is bundle-global, unscoped, and opt-in — it carries no session/user/tenant key, so it’s meant for a shared, non-sensitive knowledge base, which is exactly why it ships disabled by default. If you need per-caller isolation, that lives one layer up in the catalog (below). I’d rather ship an honest, inspectable v1 than a magic box.

The rest of what’s new (v0.2 → v0.5)

The memory story is the headline, but the project grew in every direction since the first release. OKF4net now ships as five NuGet packages plus a couple of standalone tools:

  • A local MCP server, OKF4net.Mcp. The okf-mcp dotnet tool exposes a bundle to Claude Desktop or Claude Code over the Model Context Protocol — now with bundle auto-discovery, so it finds your knowledge base instead of making you wire a path. Recipes for other MCP-capable editors (VS Code, Cursor, Rider…) are on the issue tracker as good-first contributions.
  • A multi-bundle catalog, OKF4net.Catalog. A catalog.json manifest, a source resolver, and full-text search across many bundles — with read-only knowledge sources and writable memory tiers (session / user / tenant) for exactly the per-caller scoping the agent provider’s v1 memory doesn’t do.
  • Machine-readable CLI output. The Native AOT okf CLI (validate/info/index/graph/parse/fmt) gained a --json flag on validate and info, so a CI step can parse structured diagnostics instead of scraping text.
  • A producer that turns a repo into a bundle. producers/OkfProducer generates a validated OKF bundle straight from an existing repository (npm/NuGet/README detection so far). It’s an early walking skeleton, but it closes the loop: you don’t have to hand-author a bundle to try the ecosystem.

Through all of it, the core constraint held: OKF4net and its CLI have zero third-party runtime dependencies — base class library only, hand-written YAML-subset parser and link scanner included. That’s what lets the CLI ship as a single self-contained Native AOT binary with no runtime to install, and it keeps the barrier to contributing low: there’s no framework to learn before you can read the code. Much of OKF4net is built with AI assistance (Claude Code), with the OKF spec and an extensive test suite — including byte-exact golden CLI captures — as the ground truth every change has to satisfy.

Come contribute

OKF4net is open source under LGPL-3.0-or-later and deliberately welcoming to first contributions — no prior OKF knowledge needed. The good first issue label names the files to touch and the test that should go green, ROADMAP.md shows where it’s headed, and Discussions is the place to ask before you write any code. If you’d rather start by reading, the project site and docs are the friendlier way in. Your first PR is three commands away: dotnet build, dotnet test, dotnet format.

If « agents that remember things in files you can read » sounds useful, I’d love the help — and the feedback.

OKF4net is built and maintained by Coderise.

I ported a knowledge-format (OKF) library to zero-dependency .NET — here’s what I learned

If you can cat a file, you can read the knowledge base. If you can git clone a repo, you can ship it. No vector database to stand up, no proprietary export format, no vendor lock-in — just a directory of markdown files with YAML frontmatter that a human can open in any editor and an agent can read with ReadFile. That’s the whole pitch, and it’s the reason I spent the last few weeks porting a Rust library to C# to get it onto .NET.

The format behind that pitch is Google’s Open Knowledge Format (OKF) v0.1, and the library is OKF4net (docs & project site) — a zero-dependency .NET (C#, net10.0) implementation, plus an optional layer for wiring OKF bundles straight into agents built on the Microsoft Agent Framework. This post is the launch story: what OKF actually is, why I ported it instead of writing a wrapper, and what « zero dependency » really costs and buys you.

What OKF is

OKF defines a bundle: a directory tree of UTF-8 markdown files, where each file is a concept — a YAML frontmatter block followed by a markdown body. Concepts cross-link each other with ordinary markdown links, index.md files give you progressive-disclosure directory listings, and log.md files record date-grouped change history. The only hard conformance requirement is a non-empty type field on every concept; everything else — unknown types, unknown keys, broken links — has to be tolerated by a conformant consumer. It’s deliberately boring as a format, which is the point: the format is context here, not the pitch. The pitch is what you get to do with plain files — diff them, review them in a PR, grep them, back them up with nothing but git.

The port story

This repository used to ship a Rust implementation of OKF. I removed it at commit d20343c — but only after proving, file by file and command by command, that the C# port produced byte-identical output. tests/fixtures/golden/ holds five golden captures taken directly from the Rust binary’s stdout — validate, info, graph --dot, fmt, and index — against a shared example bundle, and the C# CLI is diffed byte-for-byte against every one of them in CI. As of today the full suite passes end-to-end, including five byte-exact golden CLI comparisons against the original captures.

I’ll say the quiet part out loud: this port was AI-assisted, done largely with Claude Code driving the migration file by file, spec section by spec section, with the golden fixtures as the ground truth it had to match exactly. I think that’s worth stating plainly rather than glossing over — a byte-exact port across languages is a fairly mechanical, well-specified translation task with an unambiguous pass/fail signal (does the output match the captured bytes, yes or no), which is exactly the kind of task where an AI pair-programmer earns its keep and where you can trust the result because you can verify it byte-for-byte rather than having to take anyone’s word for it. The interesting design decisions — the YAML subset, the permissive-loading philosophy, the two-tier validation split — came from following the spec and the Python reference implementation; the AI assistance was in the grinding, get-every-byte-right execution, not the architecture.

Show, don’t tell

Here’s the library, loading a bundle and running a conformance check:

using OKF4net;

var bundle = Bundle.Load("./my_bundle");
Console.WriteLine($"{bundle.Count} concepts");

// Conformance check (§9).
var report = BundleValidator.Validate(bundle);
if (report.IsConformant)
{
    Console.WriteLine($"conformant with OKF v{OkfSpec.Version}");
}

// Traverse the cross-link graph.
var id = ConceptId.Parse("tables/orders");
foreach (var link in bundle.LinksFrom(id))
{
    Console.WriteLine($"{id} -> {link.Target} (exists: {link.Exists})");
}

Bundle.Load never aborts on a malformed concept file — it collects parse failures into bundle.ParseErrors and keeps walking the tree, because a knowledge base that one bad file can take down entirely is a bad knowledge base.

And here’s the CLI, which is the same tool the Rust binary used to be, invocation-for-invocation:

okf validate ./bundles/ga4
okf graph ./bundles/ga4 --dot | dot -Tsvg > graph.svg

okf validate exits non-zero on a non-conformant bundle, so it drops straight into a CI step. The CLI ships as a self-contained, Native AOT single-file binary — no .NET runtime install required on the machine that runs it.

The agents angle

The reason I care about this format enough to port a whole library for it is OKF4net.Agents, which turns an OKF bundle into tools and context for the Microsoft Agent Framework. OkfBundleTools wraps one bundle root and exposes nine function tools — read, browse, graph, search, write, append-log, regenerate-indexes, validate, changes-since — that an AIAgent can call directly:

var tools = new OkfBundleTools("./my_bundle");
AIAgent agent = chatClient.AsAIAgent(tools: tools.GetTools());
var response = await agent.RunAsync("Search the bundle for concepts about refunds.");

Layer OkfContextProvider onto the same tools instance and, opted in explicitly, an agent’s exchanges get captured as long-term memory — one markdown concept per UTC day, written through the same validated, lock-protected write path the tools use, plus a matching log.md entry. That’s the part I think is genuinely different from the usual answer to « give my agent memory »: instead of an opaque vector store you can’t audit, memory is a markdown file in a git-tracked directory. You can open it, diff it across commits, redact a line, or point a second agent at the exact same directory with no export step. It’s not a fit for every use case — the README is upfront that v1 memory is bundle-global and unscoped, so it’s opt-in and meant for a shared, non-sensitive bundle rather than a multi-tenant deployment — but for a single team’s shared knowledge base, « memory you can git blame » is a real capability, not a slogan.

Design choices

The whole library — OKF4net and OKF4net.Cli — has zero third-party runtime dependencies: no YAML library, no CLI-parsing package, nothing. It has its own documented YAML subset parser (frontmatter is scalars, lists, and shallow maps — no anchors, no tags, no multi-document files, and it says so with a clear error if you hand it those), its own markdown link scanner, and its own argument parsing, all on top of the .NET base class library. That constraint is what makes the CLI publishable as a single-file Native AOT binary with no runtime to install, and it’s what keeps the barrier to contributing low — there’s no framework to learn before you can read the code. OKF4net.Agents is the one exception, since talking to Microsoft.Agents.AI requires depending on it; everything else stays dependency-free by design, enforced project by project. The project also ships OKF4net.Catalog, a local multi-bundle catalog with search-by-source resolution, and OKF4net.Mcp, an MCP server that plugs a bundle straight into Claude Desktop or Claude Code — so agents and tools have a ready path to discover and query bundles without writing that plumbing themselves.

Come contribute

OKF4net is young and I’d rather it stay welcoming than gate-kept. You don’t need any prior OKF knowledge to help — the good first issue label names the files to touch and the test that should go green when you’re done, ROADMAP.md lays out where the project is headed, and Discussions is the place to ask a question before you write any code. The project is licensed LGPL-3.0-or-later, and the bar to your first PR is exactly three commands: dotnet build, dotnet test, dotnet format. If any part of « knowledge bundles you can cat and agents that remember things in files you can read » sounds useful to you, I’d love the help — and the feedback.