Your bundles get names: okf 1.8.0
🇧🇷 Ler em portuguêsUntil this release, every okf verb took a path. That was fine on day 0, when the bundle lived next to your prompt. But knowledge does not stay in one bundle: there is the handbook, the service docs, the team wiki, the repo you were in an hour ago. Paths are relative to where you are standing, and you are always standing somewhere else. A path says where a bundle is. okf 1.8.0 lets you say what it is.
Register once, name it forever
$ okf registry set ./docs --as handbook
registered handbook → /Users/you/work/billing/docs (37 concepts)
That name now works everywhere. The registry is a plain JSON file at $OKF_HOME/registry.json (default ~/.okf): read it, grep it, hand-edit it, check it into your dotfiles if you like. There is no database and no daemon, which is the whole design. It stores a path, a slug, and a title per bundle, and nothing else, so nothing can go stale except the path itself. Your bundles stay in the repos that own them; the registry only knows their names.
The naming grammar has one rule worth knowing: the gem may invent a name, but it may never substitute one you chose. A slug minted from a directory basename quietly takes a suffix on collision (docs becomes docs-2), because you never asked for that name. A name you chose with --as or rename is refused on collision instead, because silently answering to a different one would be a lie.
@slug, anywhere a directory goes
Wherever a verb takes a <dir>, @slug now goes:
okf lint @handbook # from any directory on the machine
okf render @ -o graph.html # bare @ is the registry default
okf server @handbook @wiki # a hub of exactly these two
The default is not a setting you configure. The registry is ordered, and the first entry still on disk is the default: the bundle a bare @ names and a bare okf server opens. okf registry default @slug moves an entry to the front, a rename keeps its position, a del promotes whatever is next. Nothing extra is stored, so there is no stored thing to drift, and the file cannot name a default that is not in it.
A bare okf server means something now
okf server reads its mode from its arguments. One bundle is the classic page at /. Several mount behind a hub, each at /b/<slug>/. And no arguments at all serves the whole registry, default at /. One command now opens everything you know.
Behind the hub, /b/ is a browsable index of the hosted bundles, an unknown slug answers with a page that lists the way home instead of bare text, and Cmd/Ctrl-K opens a switcher, so moving from the handbook to the wiki never leaves the page. The hub reads its bundles at boot; register a new one and restart, which is the honest trade for a server that never re-scans your disk per request.
Search stopped caring which bundle knows
The question that actually sends you across bundles is "where is this documented?", and okf search now answers it in one query:
$ okf search @all rate limit
Search — @handbook @wiki · rate limit (2 of 40 concepts)
@handbook runbooks/rate-limits Rate-limit runbook · Runbook · title
@wiki onboarding Onboarding · Note · body
Rankings merge across bundles and every row is labeled by the bundle that answered, so "which bundle knows about X" costs one command instead of one command per bundle. @all tolerates a registered directory that has vanished, with a note; naming a bundle explicitly insists on it. Asking for everything is forgiving, asking for something is strict.
The page grew into its keyboard, and into every screen
The graph page took its own step this release. The Cmd/Ctrl-K palette now opens in every mode, not only behind a hub: views are always in the list, bundles join when there are bundles to switch to. / jumps to the current view's search, and ? answers with a sheet of every shortcut, written against the key handler it documents, so it cannot drift from what the keys do. In the inspector, a concept's type and tags are now filter handles, and its link lists read as typed concepts, each with the color dot its node wears in the graph.
The same page now travels. One template serves a phone, a tablet, and a desktop: at phone widths the navigation rail becomes a drawer, the toolbar folds into a settings sheet, panels go full-bleed, and the file list collapses to its tab bar. The breakpoint tracks the width available to the page, not a device class, so rotating a tablet re-evaluates the layout and refits the graph instead of leaving you in the wrong one.
The quiet part: names are ecosystem groundwork
Here is why the registry matters beyond convenience. The direction we keep pointing at is a machine where any agent can query any bundle, over a standard interface such as MCP, without a file crawl. Every version of that future starts with the same prerequisite: the machine has to know what bundles it has and what they are called. That is exactly what $OKF_HOME/registry.json is, a contract any process can read, in a format you can already inspect with cat.
okf search @all is that future in miniature, one process at a time: federated retrieval over every bundle the machine knows. Nothing to announce today, and nothing you have to wait for either, because the thing that makes the future possible is the same thing that makes today's search work.
No Ruby on the box? The hub is in the 1.8.0 image
Every release publishes a matching multi-arch image to the GitHub Container Registry, and 1.8.0 is no exception. ghcr.io/serradura/okf:1.8.0 (also :1.8 and :latest), built for linux/amd64 and linux/arm64, carries the registry and the hub, so the multi-bundle graph runs with no Ruby on the host. Mount the bundles and serve them behind one hub:
docker run --rm -v "$PWD:/data" -p 8808:8808 \
ghcr.io/serradura/okf:1.8.0 server handbook wiki --bind 0.0.0.0
Open http://127.0.0.1:8808, then Cmd/Ctrl-K to move between them. The Docker guide also has the one-command install that drops the docker run prefix, so you type okf server handbook wiki like the native CLI.
Also in 1.8.0
A stack of smaller things shipped with the release: okf <command> -h prints that command's own banner on stdout like any other output, a file the reader cannot open no longer takes the whole bundle down (it is skipped and reported under §9.1 with its errno), lint and the read views now bucket a blank type identically, and the registry hardening that a week of adversarial review earned. The changelog has the full list.
Upgrade, register the bundles you already have, and open them all with one command. The registry page has the grammar; the demo shows the page it feeds.