Eight subcommands over a bundle or a file, a self-contained Native AOT binary with no runtime to install. validate exits non-zero on a non-conformant bundle, so the whole tool drops into CI as one line.
| Command | Argument | Does |
|---|---|---|
| validate | <bundle> | Check a bundle against OKF v0.2 conformance (§11) |
| audit | <bundle> | Report trust, freshness and lifecycle across the bundle (§5.3–§5.5) |
| info | <bundle> | Summarize concepts, types, links and version |
| index | <bundle> | (Re)generate every index.md (§8) |
| graph | <bundle> | Print the cross-link graph; --dot for Graphviz |
| parse | <file> | Parse one document and print its structure |
| fmt | <file> | Normalize by parse + re-serialize (-w writes) |
| render | <bundle> --out <dir> | Generate a browsable static HTML site from the bundle |
Global options: -h/--help prints usage; -V/--version prints the build and spec version; --as-of <YYYY-MM-DD> pins today's date for validate and audit.
Everything after a -- separator is an argument, never an option — which is how a path beginning with - is passed. The rule holds for every verb and every flag, so okf fmt -- notes.md -w treats -w as a second filename rather than as the write-in-place flag; write it as okf fmt -w -- notes.md if that is what you meant. A value belonging to an option is likewise only ever a value: in okf audit b --type --stale, --stale is the type being searched for, not a filter.
$ okf --version okf 0.5.0 (OKF spec v0.2)
Loads the bundle, runs the §11 conformance check, and prints every diagnostic followed by a tally. Exits 0 when conformant, 1 otherwise. Warnings never break conformance — only errors do — so recommended-but-missing fields are surfaced without failing the build.
$ okf validate tests/fixtures/appendix_a [warning] tests/fixtures/appendix_a/tables/users.md: missing recommended frontmatter field `description` [warning] tests/fixtures/appendix_a/tables/users.md: missing recommended frontmatter field `timestamp` 4 concept(s); 0 error(s), 2 warning(s), 0 info. ✓ conformant with OKF v0.2
--json emits the same diagnostics as machine-readable, camelCase JSON — one object per line, no pretty-printing. Each diagnostic carries a stable code (DiagnosticCode) and, where relevant, a field naming the frontmatter key involved, so a caller can branch on the finding without parsing prose.
$ okf validate tests/fixtures/appendix_a --json
{"bundle":"tests/fixtures/appendix_a","conformant":true,"conceptCount":4,"errorCount":0,"warningCount":2,"infoCount":0,"diagnostics":[{"severity":"warning","code":"MissingRecommendedField","path":"tests/fixtures/appendix_a/tables/users.md","conceptId":"tables/users","field":"description","message":"missing recommended frontmatter field `description`"}, …]}--as-of <YYYY-MM-DD> pins the date the §5.5 staleness warning is evaluated against. Without it that one diagnostic depends on the day the command runs, so a pipeline asserting on validate's output should pin it rather than let the calendar move underneath.
Answers questions about the bundle as a whole: how much of it is human-reviewed, what has passed its stale_after instant, what is deprecated. Counts always describe the whole bundle while the worklist describes the selection — audit is a worklist, not an inventory. Always exits 0: a stale concept is editorial hygiene, not a conformance failure.
$ okf audit bundles/acme_retail --as-of 2027-06-01
bundle: bundles/acme_retail
as of: 2027-06-01
concepts: 9
trust:
8 human-reviewed
0 machine-confirmed
1 unverified
status:
0 draft
8 stable
1 deprecated
stale: 7 of 9 past stale_after
needs attention (7):
computations/gross-margin-period stale 2026-12-31 human-reviewed stable
computations/revenue-ytd stale 2026-12-31 human-reviewed stable
metrics/gross-margin stale 2026-12-31 human-reviewed stable
metrics/revenue stale 2026-12-31 human-reviewed stable
policies/margin-standard stale 2026-12-31 human-reviewed stable
policies/revenue-recognition stale 2026-12-31 human-reviewed stable
tables/orders stale 2026-12-31 human-reviewed stableWith no filter flag it selects exactly what --stale selects and prints the summary above. With any of --stale, --trust, --status or --type it prints one line per matching concept and nothing else, so the output pipes. --as-of pins the observation date to midnight UTC on that day (it never changes the mode), and --json always emits the full document.
# any filter flag switches to one line per concept — pipe-friendly
$ okf audit bundles/acme_retail --trust unverified
skills/run-on-bq no-stale-after unverified stableThe question this exists for — which concepts are past their stale_after instant and have never been verified by a human? — is --stale --trust unverified,machine-confirmed: both tiers, because “machine-confirmed” also means no human ever looked.
Reports the bundle root, declared OKF version (if any), concept count, reserved-file counts, a breakdown by type, and the internal link total with broken-link count. Unparseable files are listed at the end. Always exits 0.
$ okf info tests/fixtures/appendix_a
bundle: tests/fixtures/appendix_a
concepts: 4
index.md: 0
log.md: 1
types:
1 BigQuery Dataset
3 BigQuery Table
links: 5 internal (0 broken)--json works here too, for scripting against the same summary.
$ okf info tests/fixtures/appendix_a --json
{"bundle":"tests/fixtures/appendix_a","okfVersion":null,"conceptCount":4,"indexFileCount":0,"logFileCount":1,"types":{"BigQuery Dataset":1,"BigQuery Table":3},"linkCount":5,"brokenLinkCount":0,"parseErrors":[]}Regenerates every index.md directory listing in the bundle and prints each path written, then a total. On an empty bundle it prints no index files written (empty bundle?). Exits 0. This command writes files — run it after adding or renaming concepts.
$ okf index ./my_bundle wrote my_bundle/index.md wrote my_bundle/tables/index.md 2 index file(s) regenerated.
Prints each concept's outgoing links. Resolved links use ->, broken ones -x. Pass --dot to emit Graphviz DOT (broken edges dashed and red) — pipe it straight into dot.
$ okf graph tests/fixtures/appendix_a datasets/sales -> tables/orders -> tables/customers tables/customers -> tables/orders tables/orders -> datasets/sales -> tables/customers
$ okf graph tests/fixtures/appendix_a --dot
digraph okf {
rankdir=LR; node [shape=box, fontsize=10];
"datasets/sales" -> "tables/orders";
"datasets/sales" -> "tables/customers";
"tables/customers" -> "tables/orders";
"tables/orders" -> "datasets/sales";
"tables/orders" -> "tables/customers";
}
$ okf graph ./my_bundle --dot | dot -Tsvg > graph.svgParses a single concept document (strict UTF-8) and prints its frontmatter keys, whether it has a non-empty type, the body size in bytes, and any links and citations. Exits 0 if the document is conformant, 1 if not.
$ okf parse tests/fixtures/appendix_a/tables/orders.md frontmatter (6 key(s)): type: BigQuery Table title: Orders description: One row per completed customer order. resource: https://console.cloud.google.com/bigquery?… tags: [sales, orders] timestamp: 2026-05-28T00:00:00Z has non-empty `type`: true body: 99 byte(s) links (2): [Absolute] sales dataset -> /datasets/sales.md [Absolute] customers -> /tables/customers.md
Parses the document and re-serializes it — normalizing frontmatter and block structure while preserving unknown keys byte for byte. Prints to stdout by default; -w (or --write) writes back in place. Exits 0.
$ okf fmt tests/fixtures/appendix_a/tables/orders.md --- type: BigQuery Table title: Orders description: One row per completed customer order. resource: https://console.cloud.google.com/bigquery?p=acme&d=sales&t=orders tags: - sales - orders timestamp: 2026-05-28T00:00:00Z --- # Schema Part of the [sales dataset](/datasets/sales.md). FK to [customers](/tables/customers.md).
Generates a browsable HTML site from the bundle: one page per concept (a frontmatter table, in document order, with unknown producer keys preserved, followed by the rendered body) plus a generated index page built from the same logic as okf index. Inter-concept links are rewired to the generated pages, with backlinks (“Referenced by”) added on each target page; a link to a concept that doesn't exist is flagged and left non-clickable rather than silently dropped or pointed at a 404. External links and in-page anchors are left untouched.
$ okf render tests/fixtures/appendix_a --out ./site wrote 8 files to ./site $ ls ./site assets/ datasets/ index.html tables/
The output is self-contained — it opens straight from the filesystem (file://) with no server required. Markdown renders client-side, via a vendored copy of marked v15.0.12 (MIT, credited in NOTICE); a DOM sanitizer strips anything the fixed template doesn't expect. GFM task list items survive sanitization as real, disabled <input type="checkbox"> elements with the correct checked state, so a screen reader announces them as checkboxes rather than as decorative text.
There's no full-text search in this slice — a static site has no server to run the shared ConceptSearch scorer against, and duplicating its ranking logic in JavaScript would fork the one place that scorer is meant to live. Search arrives with the planned okf serve companion (the live-server half of this work). Backed by the new, zero-dependency OKF4net.Viewer project, which ships inside the okf binary — it isn't published as a separate NuGet package.
| Code | Meaning |
|---|---|
| 0 | Success — or, for validate/parse, conformant |
| 1 | Non-conformant, missing argument, unknown subcommand, or I/O error |
Because the binary is self-contained, a CI image needs no .NET runtime, no SDK, no restore:
# any pipeline — fail the build on non-conformant knowledge
okf validate ./bundles/ga4→ library.md — the same engine, as a C# API · getting-started.md
On Windows, install via winget:
$ winget install Coderise.OKF4net
On any OS, build it from source:
$ git clone https://github.com/jchable/okf4net
$ dotnet publish src/OKF4net.Cli -c Release # self-contained okf binary