Open Knowledge in plain HTML. A note is a complete, self-describing HTML document — nothing to export, nothing to convert.
Instead of Markdown + YAML front matter or a JSON sidecar, each note is one HTML file: the body is readable content, the head carries identity and metadata, and ordinary links are the graph. The link graph is the knowledge graph. HTML is the data contract, not an export format.
One file. On the left, the document exactly as it is stored on disk. On the right, that same file opened in a browser — because it is a web page. This document was produced by the reference gem; the snippet that emitted it is in Getting started.
<!DOCTYPE html> <html lang="en"> <head profile="https://br3nt.github.io/okf/"> <meta charset="utf-8"> <title>Sourdough Starter</title> <link rel="canonical" href="/n/sourdough-starter"> <meta name="uuid" scheme="UUID" content="d4013d9b-0ecc-4682-bb13-42e0b27746aa"> <meta name="created" scheme="ISO8601" content="2026-07-02T13:23:18+00:00"> <meta name="updated" scheme="ISO8601" content="2026-07-02T13:23:18+00:00"> <meta name="keywords" content="baking, bread"> </head> <body> <p>Feed daily. See <a rel="related" href="/n/bread">bread</a></p> </body> </html>
Feed daily. See bread.
No renderer required to read it — every device already ships one.
A conforming tool goes further: it resolves the rel="related" edge, honours
the uuid, and can rebuild its whole index from a folder of files like this.
The bread link above is stubbed to the spec for this demo; in a real store
it resolves to /n/bread.
The web platform already is a knowledge format. Links are the graph;
<meta> and <link> are the metadata; the DOM is the query
model; every device ships a renderer. OKF/HTML adds no new file type and no semantic-web
toolchain — no RDF, OWL or SPARQL. Readers, human and machine, infer structure from HTML directly.
<a>, <link> and <meta>. An untyped link is a mere reference; add a rel and it asserts a relationship.chapter, contents, glossary, rev). Browsers may ignore them; conforming tools must not.author, chapter, recipe), not a slot in an anonymous container (first, next, up). Order is derived from a collection's list, not stamped on its members.Six primitives, all made of ordinary HTML. Each is defined in the spec and covered by the language-neutral conformance fixtures.
Identity and metadata live in the <head>: a uuid, a
canonical address, timestamps, tags — each typed with a scheme.
<meta name="created" scheme="ISO8601" content="2026-06-18T10:00:00Z">
rel)An edge is a link with a rel that names the target's role. Visible edges are
<a> in the body; document-level ones are <link> in the head.
<a rel="related" href="/n/pantry-stock"> pantry stock</a>
rev)The outbound <a> is authoritative; its inverse is mirrored on the target
as a rev link, so a file knows who links to it without the database.
<!-- mirrored in the target's head --> <link rev="chapter" href="/n/chapter-one">
A named note that owns its membership as an <ol> (ordered) or
<ul>. Members declare a role; the collection declares order. Tags are the
flat, anonymous case.
<ol> <li><a rel="chapter" href="/n/tides">Tides</a></li> </ol>
A profile is itself an HTML document that defines the rel terms it governs
(inverse, target-type, cardinality). <head profile> names the ones in force.
<head profile="https://br3nt .github.io/okf/">
A note used as a prototype. Its head declares relationships to other templates — ported from Rails associations — so the "add related note" UI is generated, not hand-built.
<link rel="okf:has-many" href="/templates/chapter" data-as="chapter" data-ordered="true">
rel) are mirrored as
rev. A relation's human inverse name (chapter ↔ contents)
is a vocabulary concern; the raw mirror always reuses the authoritative keyword. The reconciler
keeps the mirrors, collection order, and delete-cascades in sync — authority wins on conflict.There is a working Ruby reference implementation today, and a language-neutral spec plus conformance fixtures for ports in any language that can parse HTML.
One repo, two gems that version together: okf-html (the pure-Ruby format
core — serializer, vocabulary, templates; no Rails, no I/O) and okf-html-rails
(a mountable engine that wires the core into a host app). Reference gem v0.1.4,
tracking spec draft v0.1.
# Gemfile — one repo, two gemspecs gem "okf-html", git: "https://github.com/br3nt/okf-html.git", glob: "okf-html/*.gemspec" gem "okf-html-rails", git: "https://github.com/br3nt/okf-html.git", glob: "okf-html-rails/*.gemspec"
Hosts go through the Repository facade — never
hand-write heads, slugs, or rev mirrors. This exact snippet produced the note
at the top of the page:
require "okf/html" repo = OKF::Repository.new(store: OKF::Store::Memory.new) note = repo.create( title: "Sourdough Starter", content: %(<p>Feed daily. See <a rel="related" href="/n/bread">bread</a>.</p>), tag_names: ["baking", "bread"] ) puts repo.store.read(note.uuid) # => the document shown above
In Rails, include the container concern in whatever owns notes and get a scoped repository for free:
class WorkspaceNode < ApplicationRecord include OKF::Container end node.okf.create(title: "Idea", content: "<p>…</p>", tag_names: ["draft"]) node.okf.find(uuid_or_slug) # => OKF::Note
OKF/HTML is a format, not a framework. Anything that can parse HTML can read and write it — Python, JS, Go, Rust, whatever. Two artefacts get you there without reading Ruby:
rev mirrors, collections, vocabulary, templates, the reconciler, and a conformance checklist (reader / writer / reconciler).render.yml gives attribute bags and the exact HTML a conforming renderer must emit; parse.yml gives stored documents and the fields a parser must extract. Compare rendered HTML as an exact string, including the final newline.Bring up a port by making render.yml pass, then parse.yml. A case
added to the fixtures adds coverage to the reference suite automatically, so the vectors and
the implementation can't drift apart.
Because a note is plain, self-describing HTML, an agent is a first-class consumer: no bespoke parser, no export step. The format ships machine-readable entry points on purpose.
A short manifest pointing at the spec, the implementer and hypermedia guides, the agent skill, and the conformance fixtures — the map an agent reads first.
SPEC.md is written to be followed literally; the fixtures are executable ground truth an agent can check its output against.
An okf-html skill (.claude/skills/okf-html/ in the repo) teaches
an agent to author and manage notes — typed links, collections, templates, vocabulary — as
plain HTML.
The engine leans on HTML as the wire format end to end: notes are documents, and the editor and graph ship as no-build components. HTML is what crosses every boundary.
This URL — https://br3nt.github.io/okf/ — is the OKF/HTML base
profile: the format's stable identifier. Every conforming document declares it
on <head profile="…"> (composed with any user-defined profiles), and it is
embedded verbatim in every rendered note — it is not a placeholder and does not change.
A document that references this URL is asserting: "my rel values,
rev mirrors, collections and metadata mean what the OKF/HTML spec says they mean."
The human-readable profile vocabulary page lives here; the canonical, authoritative definition of
the terms is the spec.
SPEC.md in the okf-html repo.
Base relation catalogue and the naming rule: SPEC §6. The <head> profile
declaration: SPEC §2.2.