okf-render turns a bundle into a static site you open straight from the disk: a tree explorer, a Jump to palette, one page per concept with its trust and staleness at a glance, and a graph of how the concepts link. No server, no network, read-only.
Point okf-render at a bundle and name, with --out, the directory to write the site into:
$ okf-render bundles/acme_retail --out ./acme-site
# then open ./acme-site/index.html in any browser — double-click it, no server neededThe output is a folder of plain files: an index.html at the root, one .html page per concept at the concept's own path (computations/gross-margin-period.html), a graph.html for the whole bundle, and an assets/ folder holding the scripts, the stylesheet and the fonts. Nothing is fetched from the network when a page opens, so the folder works from file://, from a USB stick, from a web server or from a documentation host alike. The pages below are all of bundles/acme_retail, the sample bundle in the repository.
okf-render is its own binary, separate from okf: the validator that runs in CI stays small, and only the site generator carries the vendored marked (MIT) that renders the markdown in the browser and the three embedded font families (SIL OFL 1.1). Both licences ship beside the binary and inside every generated site, under assets/licenses/.
Each GitHub Release carries an okf-render archive for Windows, Linux and macOS, on x64 and arm64. On Linux and macOS the install script fetches the right one and verifies its SHA-256: pass --bin okf-render to install the site generator instead of okf (the script is described in docs/cli.md). On any OS you can also build it from a checkout with dotnet publish src/OKF4net.Render -c Release, which needs the .NET SDK 10.0 or later and produces a self-contained Native AOT binary.
okf-render --help prints the usage, --version the build. The site is a snapshot: render again after the bundle changes.

acme_retail, with the Neighbourhood graph set to two hops. The numbers are listed below.status, its trust tier with who verified it and when, and its staleness. What they mean.From 1,100 px of window width the three columns sit side by side; below that they stack, the page first, then its context, then the explorer, so nobody scrolls through a tree to reach the text.
The right-hand column — the context panel of a concept page, the detail drawer of the global graph — is 340 px wide by default. A thin bar on its left edge, the splitter, resizes it, so a graph can have more room. Drag it with a mouse, a finger or a pen: the column follows the pointer, and Esc during a drag puts the width back. The explorer and the facets column are not resizable.

separator named Resize the side panel, with its width in pixels as its value). With it focused, ← widens the column and → narrows it by 16 px, 64 px with Shift; Home and End go to the narrowest and the widest; Enter or a double click restores the default width. Combinations with Alt, Ctrl or ⌘ are left to the browser.file:// pages, the splitter still works but forgets.The left column lists the bundle the way its directories do. A folder opens and closes on its own; a concept that is also a folder opens and expands separately. Rows are labelled with the last segment of the id, and the title is the tooltip.

margin typed in the filter.stale_after. A concept with no verified key has no trust mark.Press Ctrl+K or / on any page (or press the Jump to a concept… button) and type. The palette matches what you type against concept titles, ids and tags, lists the best matches with their type shape, title and id, and opens the one you choose. ↑/↓ move, Enter opens, Esc closes and returns focus to where it was.

margin.The ranking is a fixed order of tiers over those three fields, not a weighted score, and it never reads the body of a concept: see What it does not do.
The centre column is the concept itself: a breadcrumb, the title (a leading # heading that only repeats it is dropped), the chips, the frontmatter box and the markdown body, rendered in the browser. Links between concepts work as links, and a link to a concept the bundle does not contain is flagged as broken. Headings get generated anchors, so an author's own #usage link lands.

## and ### headings and marks the section you are reading as you scroll. A concept with no such heading has no list.Enlarging it. The icon button beside the 1 hop / 2 hops toggle, Enlarge the neighbourhood, opens the same graph in a dialog, up to 1,100 × 760 px, for a neighbourhood too busy for the panel.

While the dialog is open, Tab and Shift+Tab move among its own controls and wrap round, the page behind is inert and does not scroll, and / and Ctrl+K do nothing. Below 760 px of window width the dialog fills the window and the list goes under the drawing.

Global graph in the header opens graph.html (graph-1.html if a concept already uses the name), a page that draws every body link of the bundle — the edges okf graph counts — with the arrows pointing at the target. The layout is computed in your browser, and it is deterministic: the same bundle gives the same starting layout on every conforming browser.

metrics/gross-margin selected. The numbers are listed below.
finance chosen and unmatched concepts dimmed instead of hidden.Large bundles. Above 1,500 visible nodes (concepts and absent targets) the graph is not drawn at all: the list is shown and the status line asks for narrower filters. The list is also there whenever you want it, with the same facets applied.
Addresses. Selecting a concept writes graph.html#<concept id> to the address bar without adding a history entry, and opening such an address selects that concept. A concept page's Global graph link carries its own id, so it lands on that concept.
Keyboard. The drawing is a single tab stop. Inside it, Page Up and Page Down move through the nodes in index order, Home and End go to the first and the last, an arrow key goes to the nearest neighbour in that direction, Space selects and Enter opens the concept's page.
The pages follow your system's light or dark setting. The moon button in the header overrides it for that site and remembers the choice in the browser's local storage; where storage is unavailable (some browsers refuse it for file:// pages) the page simply keeps following the system.

On a narrow window the header wraps onto several rows and the columns stack as described above.

Every mark comes from the concept's own frontmatter; okf-render reads it through the same audit that okf audit runs.
| Mark | Means (and where the spec says so) |
|---|---|
| human-reviewed | verified holds at least one entry whose actor is human:<id> (§5.2, §5.3) |
| machine-confirmed | verified holds entries, but only from actors that are not human: (§5.2, §5.3) |
| unverified | No verified key, so no trust mark is drawn (§5.3) |
| stale after D / stale since D | stale_after is a date still to come, or already reached (now ≥ stale_after); the hourglass marks the second (§5.5) |
| status chip | draft, stable or deprecated, as the concept declares it (§5.4) |
The trust tier shows who a concept says reviewed it and when, not a proof that they did: the spec calls these tiers advisory signals, not access control, and okf verify cannot authenticate the actor it is given. Staleness is judged as of reading: the page compares stale_after with your browser's clock when it opens, and again when the tab becomes visible again, so a page rendered in March can be stale in June without being rendered again.
aria-current.ConceptSearch scorer in, and copying its weights into JavaScript would fork it. The palette finds concepts by title, id and tag; to search bodies, use okf-mcp or the library (mcp.md, library.md).okf serve, and none is planned. Render again to see a change.→ cli.md — the okf commands that check the same bundle · getting-started.md — write one first