okf 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1f08bf7a4a1a49ef0bea5953b380df85bdbe79e1b30998e0d168d122d4a20c2
4
- data.tar.gz: 97aff0312275def9b514c0e50bcc3872a73e3a304efa7ac8d85046a846711a7f
3
+ metadata.gz: 464529d862280b3385f00fa36a309c81fa03f71db2d2534f11707a04fdca4679
4
+ data.tar.gz: 1487c71ef1e70340d0a6212b85f0202ae287c0a1df7b59489f643bdbf63dd05f
5
5
  SHA512:
6
- metadata.gz: '09c1784bee88f5a74087345fa3ee26a13ba2875ff9447559852c23cf294f6f05ffa457eabdd67cbbac4bc5e255b3cc91f128863fab92d9b5a88f3ae41062e871'
7
- data.tar.gz: d26885ca8683d2de6ad036d7a158089ba4b7e12437131b617687bad51bd15157f64dd148bcd7f8f502a95ae48d644f9c5674464e82ff3f97bc34faf61a24dc24
6
+ metadata.gz: 6240c78b9b1c23b64c97f980db2aabe5d3126eb0c69e4eb853381bed2e5e1be991a4835c62a806cfb5e219fdb4b3bf4bf3ef8960063d6f30d5fcf2aaf630d8d2
7
+ data.tar.gz: 32d943f353984a9d6e7224c881169bf851008976adee4ef6e785878372f225162f4359021b2ab576cf0615dde0b23054b61826ee39b8937356e1759d0dbca0c4
@@ -4,14 +4,15 @@ title: Interactive graph server (server)
4
4
  description: A self-contained HTML knowledge graph served over HTTP, and a mountable Rack app.
5
5
  resource: lib/okf/server/app.rb
6
6
  tags: [server, graph, rack, diagram]
7
- timestamp: 2026-07-11T12:00:00Z
7
+ timestamp: 2026-07-12T12:00:00Z
8
8
  ---
9
9
 
10
10
  # Overview
11
11
 
12
12
  `okf server` boots an interactive view of the [graph](../model/graph.md):
13
13
  `OKF::Server::App` is a Rack app that serves one self-contained HTML page which
14
- draws the bundle with Cytoscape and renders concept bodies with marked. Because
14
+ draws the bundle with Cytoscape and renders concept bodies with marked, sanitized
15
+ by DOMPurify. Because
15
16
  it is a plain Rack app, it also mounts inside a host application (e.g. a Rails
16
17
  route) — the built-in WEBrick runner is just the default, injected so tests drive
17
18
  it without opening a socket.
@@ -19,8 +20,9 @@ it without opening a socket.
19
20
  # The page stays self-contained
20
21
 
21
22
  One ERB template, inline CSS and JS, no build step and no bundler. The only
22
- external assets are Cytoscape and marked from a CDN — plus Mermaid, lazy-loaded
23
- only when a concept body actually contains a diagram; everything else is inlined.
23
+ external assets are Cytoscape, marked, and DOMPurify from a CDN — plus Mermaid,
24
+ lazy-loaded only when a concept body actually contains a diagram; everything else
25
+ is inlined.
24
26
  The graph draws from a **minimal** node payload and pulls each concept's body
25
27
  **on demand** via `fetch()`, which is why even a large bundle loads fast.
26
28
 
@@ -35,7 +37,7 @@ sequenceDiagram
35
37
  Note over A,B: angle brackets escaped (json_for_script) — safe
36
38
  B->>A: GET /node?id=… (on demand)
37
39
  A-->>B: concept Markdown body
38
- Note over A,B: marked renders it UNSANITIZEDtrust boundary
40
+ Note over A,B: marked renders it, DOMPurify sanitizes it safe
39
41
  ```
40
42
 
41
43
  # Endpoints
@@ -49,11 +51,12 @@ sequenceDiagram
49
51
 
50
52
  # Trust boundary
51
53
 
52
- Fetched Markdown bodies are rendered **without sanitization**, so only serve
53
- bundles you trust. Data inlined into the page is safe it goes through
54
- `json_for_script`, which escapes `<` so it cannot break out of its `<script>`
55
- but the on-demand body is not. See the
56
- [server trust boundary](../design/server-trust-boundary.md) for the full picture.
54
+ Both paths into the page are guarded. Inlined data goes through `json_for_script`,
55
+ which escapes `<` so it cannot break out of its `<script>`; each fetched body is
56
+ run through `DOMPurify.sanitize(marked.parse(...))`, which strips any script or
57
+ handler before it reaches the DOM. See the
58
+ [server trust boundary](../design/server-trust-boundary.md) for what that does and
59
+ does not cover.
57
60
 
58
61
  # Citations
59
62
 
@@ -4,7 +4,7 @@ title: Library API
4
4
  description: The Ruby surface — a pure in-memory model plus on-disk handles, an "ActiveRecord for the filesystem".
5
5
  resource: lib/okf.rb
6
6
  tags: [library, api, ruby, diagram]
7
- timestamp: 2026-07-11T12:00:00Z
7
+ timestamp: 2026-07-12T12:00:00Z
8
8
  ---
9
9
 
10
10
  # Overview
@@ -18,6 +18,11 @@ gives you two layers, split cleanly by the [core/shell rule](../design/core-shel
18
18
  - **on-disk handles** — `OKF::Concept::File` and `OKF::Bundle::Folder`, which add
19
19
  `load` / `save` / `reload` / `delete` on top of the pure model.
20
20
 
21
+ `require "okf"` stops at those two layers: the [CLI](../cli.md) and the skill
22
+ installer load only when asked for (from `exe/okf`, or an explicit
23
+ `require "okf/cli"` / `require "okf/skill"`), so an app embedding the library
24
+ never drags in the command-line machinery.
25
+
21
26
  ```mermaid
22
27
  classDiagram
23
28
  class Concept {
data/.okf/design/index.md CHANGED
@@ -7,4 +7,4 @@ the code looks the way it does.
7
7
  * [Core/shell split](core-shell-split.md) - pure logic must never touch disk, stdio, or the shell layer; a test enforces it.
8
8
  * [Ruby 2.4 floor](ruby-floor.md) - runs on the Ruby an OS already ships; newer APIs are banned.
9
9
  * [Runtime dependencies](runtime-dependencies.md) - exactly `rack` and `webrick`, no ActiveSupport.
10
- * [Server trust boundary](server-trust-boundary.md) - the served page renders concept bodies unsanitized.
10
+ * [Server trust boundary](server-trust-boundary.md) - the served page sanitizes concept bodies and escapes inlined data; both XSS paths are closed.
@@ -1,35 +1,40 @@
1
1
  ---
2
2
  type: Constraint
3
3
  title: The server trust boundary
4
- description: The served page renders concept bodies without sanitization, so only serve bundles you trust.
4
+ description: The served page sanitizes each concept body before rendering and escapes inlined data, so both XSS paths into the page are closed.
5
5
  resource: lib/okf/server/templates/graph.html.erb
6
6
  tags: [security, server, xss]
7
- timestamp: 2026-07-11T12:00:00Z
7
+ timestamp: 2026-07-12T12:00:00Z
8
8
  ---
9
9
 
10
10
  # Overview
11
11
 
12
- The [graph server](../capabilities/graph-server.md) is designed for trusted
13
- bundles. Its page renders each concept's Markdown body **without sanitization**
14
- (marked, no sanitizer), so a hostile bundle could carry active content. The rule
15
- is simple: **only serve bundles you trust.**
12
+ The [graph server](../capabilities/graph-server.md) renders whatever bundle you
13
+ point it at, and a bundle is just files, so the page has to assume a body might
14
+ carry active content. Two defenses handle that one for each path into the page.
16
15
 
17
16
  # Where the boundary sits
18
17
 
19
- There are two different data paths into the page, and only one is escaped:
18
+ There are two data paths into the page, and each carries its own guard:
20
19
 
21
20
  | Path | Handling | Safe? |
22
21
  |------|----------|-------|
23
22
  | Graph data **inlined** into the page | through `json_for_script`, which escapes `<` | yes — it cannot break out of its `<script>` |
24
- | Concept bodies **fetched** on demand (`/node?id=`) | rendered as Markdown, unsanitized | nothis is the trust boundary |
23
+ | Concept bodies **fetched** on demand (`/node?id=`) | `marked` renders the Markdown, then `DOMPurify.sanitize` scrubs it before it reaches the DOM | yesscripts, handlers, and `javascript:` URLs are stripped |
25
24
 
26
- So the XSS boundary is not the inlined data (that is handled); it is the
27
- on-demand [body](../format/cross-links.md) render. The
28
- [self-contained page](../capabilities/graph-server.md) keeps external assets down
29
- to Cytoscape and marked from a CDN, but that does not make the rendered body
30
- safe.
25
+ The [description](../format/cross-links.md) shown in the inspector takes a third
26
+ path and never needs the client's help: the server escapes it
27
+ (`OKF::Server::App#description_fragment`) before sending it, so it arrives inert.
28
+
29
+ # What sanitizing does not cover
30
+
31
+ DOMPurify removes the code, not the content. The page still fetches and shows the
32
+ links, images, and Mermaid diagrams a body names (Mermaid runs in its `strict`
33
+ mode), and it pulls Cytoscape, marked, and DOMPurify from a CDN. So the rule is no
34
+ longer *only serve bundles you trust* — it is the ordinary care you would give any
35
+ document from a source you do not know.
31
36
 
32
37
  # Citations
33
38
 
34
- [1] [README.md — Server trust boundary](https://github.com/serradura/okf-gem/blob/main/README.md) — the unsanitized-render warning.
35
- [2] [lib/okf/server/templates/graph.html.erb](https://github.com/serradura/okf-gem/blob/main/lib/okf/server/templates/graph.html.erb) — `json_for_script` and the body render.
39
+ [1] [README.md — Server trust boundary](https://github.com/serradura/okf-gem/blob/main/README.md) — the two-defense summary.
40
+ [2] [lib/okf/server/templates/graph.html.erb](https://github.com/serradura/okf-gem/blob/main/lib/okf/server/templates/graph.html.erb) — `json_for_script` and the `DOMPurify.sanitize(marked.parse(...))` render.
data/.okf/log.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Update Log
2
2
 
3
3
  ## 2026-07-12
4
+ * **Sync**: caught the bundle up with the gem at 1.1.0 — the [graph server](capabilities/graph-server.md) now sanitizes each fetched body with DOMPurify before rendering, so the [server trust boundary](design/server-trust-boundary.md) closes the on-demand render path (its [design listing](design/) reworded to match), and the [library API](capabilities/library-api.md) notes that `require "okf"` loads the library alone now that the CLI and skill load on demand.
4
5
  * **Sync**: caught the bundle up with the CLI at 1.0.0 — documented the new `index` command (the §6 progressive-disclosure map, the read view that sees the reserved `index.md` layer), compact-by-default JSON with `--pretty`, and `--fields`/`--except` projection on the list views, in [read views](capabilities/read-views.md) plus the `index`-verb enumerations in the [CLI](cli.md), the [overview](overview.md), and the [capabilities](capabilities/) index listing.
5
6
 
6
7
  ## 2026-07-11
data/.okf/overview.md CHANGED
@@ -15,8 +15,9 @@ gives you leverage over knowledge that already lives as Markdown.
15
15
 
16
16
  ```mermaid
17
17
  flowchart LR
18
- skill["companion<br/>agent skill"] -. authors .-> bundle[("OKF v0.1 bundle<br/>Markdown + YAML")]
18
+ skill["companion<br/>agent skill"] -. authors/curate .-> bundle[("OKF v0.1 bundle<br/>Markdown + YAML")]
19
19
  bundle --> model["pure model<br/>Concept · Bundle · Graph"]
20
+ skill -. execute .-> cli
20
21
  subgraph cli ["okf CLI"]
21
22
  validate["validate — legal? §9"]
22
23
  lint["lint — well-curated?"]
@@ -29,13 +30,13 @@ flowchart LR
29
30
  Over such a bundle the gem gives you five capabilities behind one
30
31
  [command-line tool](cli.md):
31
32
 
32
- | Capability | What it answers | Verb |
33
- |------------|-----------------|------|
34
- | [Conformance validator](capabilities/validator.md) | Is this a legal OKF bundle? (§9) | `validate` |
35
- | [Curation linter](capabilities/linter.md) | Is it navigable, complete, fresh? | `lint` / `loose` |
36
- | [Interactive graph server](capabilities/graph-server.md) | Can I explore it visually? | `server` |
37
- | [Library API](capabilities/library-api.md) | Can my Ruby program use it? | (in-process) |
38
- | [Companion agent skill](capabilities/agent-skill.md) | Can an agent author it? | `skill` |
33
+ | Capability | What it answers | Verb |
34
+ | -------------------------------------------------------- | --------------------------------- | ---------------- |
35
+ | [Companion agent skill](capabilities/agent-skill.md) | Can an agent author it? | `skill` |
36
+ | [Conformance validator](capabilities/validator.md) | Is this a legal OKF bundle? (§9) | `validate` |
37
+ | [Curation linter](capabilities/linter.md) | Is it navigable, complete, fresh? | `lint` / `loose` |
38
+ | [Interactive graph server](capabilities/graph-server.md) | Can I explore it visually? | `server` |
39
+ | [Library API](capabilities/library-api.md) | Can my Ruby program use it? | (in-process) |
39
40
 
40
41
  Alongside those, a family of [read views](capabilities/read-views.md) —
41
42
  `index`, `catalog`, `files`, `tags`, `stats`, `graph` — print the bundle at a
@@ -43,7 +44,7 @@ glance so an agent reads it without a browser.
43
44
 
44
45
  # The two ideas it inherits from the format
45
46
 
46
- - **Dual audience.** Every file serves a human skimming it *and* an agent
47
+ - **Dual audience.** Every file serves a human skimming it _and_ an agent
47
48
  extracting from it, so bodies are structural Markdown and
48
49
  [links](format/cross-links.md) are plain Markdown links — both readers already
49
50
  understand them.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.0] - 2026-07-12
4
+
5
+ - The graph server now sanitizes every concept body before rendering it. The
6
+ page runs marked's HTML output through [DOMPurify](https://github.com/cure53/DOMPurify)
7
+ (loaded from the same CDN as Cytoscape and marked) on the way to the DOM, so a
8
+ bundle carrying active content in a Markdown body can no longer script the
9
+ viewer. Inlined graph data was already escaped through `json_for_script`; this
10
+ closes the other path.
11
+ - `require "okf"` now loads the pure library only. The two argv-facing shells —
12
+ `OKF::CLI` and the `OKF::Skill` installer — load on demand, from `exe/okf` or
13
+ an explicit `require "okf/cli"` / `require "okf/skill"`. `optparse` moves with
14
+ the CLI, so an embedding app (e.g. a Rails store) that only reaches for the
15
+ in-memory model and on-disk handles no longer pulls in the command-line
16
+ machinery. The CLI itself is unchanged.
17
+
3
18
  ## [1.0.0] - 2026-07-12
4
19
 
5
20
  Initial release.
data/README.md CHANGED
@@ -5,17 +5,59 @@
5
5
  </h1>
6
6
  </p>
7
7
 
8
- > A rough project, cut and polished into a jewel. And like any jewel, what it is
9
- > worth comes down to what it does with knowledge: reading it, validating it,
10
- > curating it, and putting it on display.
11
-
12
- **okf-gem** reads, validates, lints, and serves **Open Knowledge Format (OKF)**
13
- v0.1 bundles. OKF is portable knowledge: a directory of Markdown files with YAML
14
- frontmatter that both humans and agents read. Each file is a _concept_; a
15
- directory of them is a _bundle_. Over such a bundle the gem gives you five
16
- things: a library API, a conformance validator, a curation linter, an
17
- interactive graph server, and a companion agent skill. All but the library API
18
- are reachable through one `okf` command-line tool.
8
+ <p align="center">
9
+ <i>A lightweight Ruby gem for OKF: author, curate, and serve bundles as an interactive graph.</i>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://rubygems.org/gems/okf"><img src="https://img.shields.io/gem/v/okf" alt="Gem version"></a>
14
+ <a href="https://rubygems.org/gems/okf"><img src="https://img.shields.io/gem/dt/okf" alt="Downloads"></a>
15
+ <a href="https://github.com/serradura/okf-gem/actions/workflows/main.yml"><img src="https://github.com/serradura/okf-gem/actions/workflows/main.yml/badge.svg" alt="CI"></a>
16
+ <a href="https://github.com/serradura/okf-gem"><img src="https://img.shields.io/badge/ruby-%3E%3D%202.4-black" alt="Ruby >= 2.4"></a>
17
+ <a href="LICENSE.txt"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="License: Apache-2.0"></a>
18
+ <a href="lib/okf/skill/reference/SPEC.md"><img src="https://img.shields.io/badge/OKF-v0.1-6E56CF" alt="OKF v0.1"></a>
19
+ </p>
20
+
21
+ **okf-gem** — `okf` on RubyGems — reads, validates, lints, and serves
22
+ **Open Knowledge Format (OKF)** v0.1 bundles: directories of Markdown files with YAML frontmatter that humans and agents read from one source. It does not define a new place to keep knowledge; it gives you leverage over knowledge that already lives as Markdown. Each file is a _concept_; a directory of them is a _bundle_.
23
+
24
+ Here is what it is able to do:
25
+
26
+ ```mermaid
27
+ flowchart LR
28
+ skill["companion<br/>agent skill"] -. authors/curate .-> bundle[("OKF v0.1 bundle<br/>Markdown + YAML")]
29
+ bundle --> model["pure model<br/>Concept · Bundle · Graph"]
30
+ skill -. execute .-> cli
31
+ subgraph cli ["okf CLI"]
32
+ validate["validate — legal? §9"]
33
+ lint["lint — well-curated?"]
34
+ server["server — explore"]
35
+ end
36
+ model --> cli
37
+ model --> library["library API<br/>embed in Ruby"]
38
+ ```
39
+
40
+ Over a bundle the gem gives you the `okf`
41
+ command-line tool (the library API is also usable in-process). Each capability
42
+ below links to the concept that documents it: this gem's own knowledge is an OKF
43
+ bundle, so you can read its design in the format it defends.
44
+
45
+ | Capability | What it answers | Verb |
46
+ | ------------------------------------------------------------- | --------------------------------- | ---------------- |
47
+ | [Companion agent skill](.okf/capabilities/agent-skill.md) | Can an agent author it? | `skill` |
48
+ | [Conformance validator](.okf/capabilities/validator.md) | Is this a legal OKF bundle? (§9) | `validate` |
49
+ | [Curation linter](.okf/capabilities/linter.md) | Is it navigable, complete, fresh? | `lint` / `loose` |
50
+ | [Interactive graph server](.okf/capabilities/graph-server.md) | Can I explore it visually? | `server` |
51
+ | [Library API](.okf/capabilities/library-api.md) | Can my Ruby program use it? | in-process |
52
+
53
+ > [!TIP]
54
+ > **Browse the gem as knowledge, not just docs.** This README is the front door;
55
+ > the depth lives in the [`.okf/`](.okf) bundle this repo ships. Start at the
56
+ > [overview](.okf/overview.md), then follow the graph into the
57
+ > [capabilities](.okf/capabilities/) (what it does), the
58
+ > [design constraints](.okf/design/) (why it stays this light), and the
59
+ > [format itself](.okf/format/) (what it operates on). Run `okf server .okf` to
60
+ > walk the same bundle as an interactive graph.
19
61
 
20
62
  It is deliberately light so it runs on the Ruby your OS already ships:
21
63
 
@@ -23,7 +65,57 @@ It is deliberately light so it runs on the Ruby your OS already ships:
23
65
  its core dependency;
24
66
  - only two runtime dependencies: `rack` (the server is a mountable Rack app)
25
67
  and `webrick` (unbundled from Ruby in 3.0);
26
- - no ActiveSupport, no build step, no JavaScript toolchain.
68
+ - no ActiveSupport, no build step, no JavaScript toolchain — the
69
+ [design constraints](.okf/design/) that hold this line are enforced by tests.
70
+
71
+ ## Why OKF
72
+
73
+ Project knowledge (why a service exists, what a metric really measures, the
74
+ reasoning a schema encodes) lives scattered across wikis, code comments, and
75
+ whoever happened to be in the room, and an agent re-derives it every session. OKF
76
+ gives it one durable, diffable home, versioned next to the code it describes and
77
+ read from the same file by people and agents alike. [OKF][okf] is an open,
78
+ vendor-neutral format (Google Cloud, 2026); this gem is the Ruby-native way to
79
+ work with it.
80
+
81
+ [okf]: https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing
82
+
83
+ ## What a bundle looks like
84
+
85
+ A bundle is just a directory; each concept is one Markdown file whose path is its
86
+ id. This repo documents _itself_ in OKF, so the tree below is real:
87
+
88
+ ```
89
+ .okf/
90
+ ├── index.md # progressive-disclosure map (root carries okf_version)
91
+ ├── log.md # ISO-dated change history, newest first
92
+ ├── overview.md
93
+ ├── format/frontmatter.md
94
+ ├── model/graph.md
95
+ └── capabilities/graph-server.md # one concept = one file
96
+ ```
97
+
98
+ The only hard requirement is YAML frontmatter with a non-empty `type`; everything
99
+ else is optional and tolerated when missing. A concept (here the real
100
+ `capabilities/graph-server.md`, body trimmed) reads:
101
+
102
+ ```markdown
103
+ ---
104
+ type: Capability
105
+ title: Interactive graph server (server)
106
+ description: A self-contained HTML knowledge graph served over HTTP, and a mountable Rack app.
107
+ resource: lib/okf/server/app.rb
108
+ tags: [server, graph, rack, diagram]
109
+ timestamp: 2026-07-11T12:00:00Z
110
+ ---
111
+
112
+ # Overview
113
+
114
+ `okf server` boots an interactive view of the [graph](../model/graph.md) …
115
+ ```
116
+
117
+ That bundle is this gem's own documentation. Clone the repo and run
118
+ `okf server .okf` to browse it as the graph diagrammed at the top of this file.
27
119
 
28
120
  ## Installation
29
121
 
@@ -56,10 +148,6 @@ okf --version
56
148
  Exit codes: `0` success, `1` non-conformant bundle (or a `lint --fail-on`
57
149
  threshold crossed), `2` usage error.
58
150
 
59
- > [!NOTE]
60
- > Section numbers like §5, §8, and §9 refer to the OKF v0.1 spec, bundled with
61
- > the skill at [`lib/okf/skill/reference/SPEC.md`](lib/okf/skill/reference/SPEC.md).
62
-
63
151
  ```bash
64
152
  $ okf validate docs
65
153
  OKF v0.1 conformance — docs
@@ -82,11 +170,13 @@ _The graph server on this repo's own [`.okf`](.okf) bundle, with the
82
170
 
83
171
  `graph` and `server` are best-effort (§9): a file with invalid frontmatter is
84
172
  skipped (and noted on stderr), not fatal, so one bad file never breaks the rest.
85
- Before serving a bundle you did not author, read the
86
- [server trust boundary](#server-trust-boundary).
173
+ The [graph server](.okf/capabilities/graph-server.md) concept walks the request
174
+ flow and endpoints; the [server trust boundary](#server-trust-boundary) below
175
+ explains how the page handles a bundle you did not author.
87
176
 
88
- `lint` reports curation quality (reachability, backlog, completeness, freshness,
89
- provenance, and hygiene) separately from `validate`. It is advisory: it exits
177
+ `lint` (the [curation linter](.okf/capabilities/linter.md)) reports curation
178
+ quality (reachability, backlog, completeness, freshness, provenance, and hygiene)
179
+ separately from `validate`. It is advisory: it exits
90
180
  `0` even with findings unless you opt into gating with `--fail-on warn`.
91
181
 
92
182
  ```bash
@@ -116,7 +206,8 @@ always exits `0`.
116
206
 
117
207
  ## Agent skill
118
208
 
119
- The gem carries the companion OKF agent skill: a `SKILL.md` plus reference
209
+ The gem carries the [companion OKF agent skill](.okf/capabilities/agent-skill.md):
210
+ a `SKILL.md` plus reference
120
211
  and template files that teach a coding agent to author, maintain, and consume OKF
121
212
  bundles and to drive the commands above. Because the skill ships inside the gem,
122
213
  installing the gem already puts the skill on your machine, and the skill's
@@ -139,7 +230,8 @@ is never clobbered.
139
230
 
140
231
  ## Library
141
232
 
142
- The gem is two layers: pure in-memory data (`OKF::Concept`, `OKF::Bundle`)
233
+ The gem (the [library API](.okf/capabilities/library-api.md)) is two layers: pure
234
+ in-memory data (`OKF::Concept`, `OKF::Bundle`)
143
235
  you build, interrogate, and analyze with no disk involved, and on-disk
144
236
  handles (`OKF::Concept::File`, `OKF::Bundle::Folder`) that add
145
237
  load/save/reload/delete, an "ActiveRecord for the filesystem".
@@ -199,7 +291,8 @@ The lower-level pieces are usable on their own too: `OKF::Bundle::Validator.call
199
291
 
200
292
  ### Conformance model
201
293
 
202
- `validate` implements the spec's [§9 conformance definition](lib/okf/skill/reference/SPEC.md#9-conformance)
294
+ `validate` (the [conformance validator](.okf/capabilities/validator.md)) implements
295
+ the spec's [§9 conformance definition](lib/okf/skill/reference/SPEC.md#9-conformance)
203
296
  exactly. There are three hard conditions, all errors:
204
297
 
205
298
  - **§9.1** every non-reserved file has a parseable YAML frontmatter block;
@@ -236,12 +329,23 @@ is the structured input an agent consumes to reason about those.
236
329
 
237
330
  ## Server trust boundary
238
331
 
239
- > [!WARNING]
240
- > The served page loads its JavaScript (Cytoscape, marked, mermaid, and layout
241
- > plugins) from a CDN and renders each concept's Markdown body **without
242
- > sanitization**, so only serve bundles you trust. Inlined graph data still
243
- > cannot break out of its `<script>` (`<` is escaped), but the fetched Markdown
244
- > is rendered unsanitized.
332
+ > [!NOTE]
333
+ > The graph page defends against a bundle that carries active content in two
334
+ > places. It escapes any graph data inlined into the page (`<` becomes `&lt;`),
335
+ > which keeps that data from breaking out of its `<script>`. And it fetches each
336
+ > concept body on demand, then runs marked's HTML output through
337
+ > [DOMPurify](https://github.com/cure53/DOMPurify) before it reaches the DOM, so
338
+ > a script or event handler hidden in a Markdown body is stripped rather than
339
+ > executed. Descriptions are escaped on the server.
340
+ >
341
+ > That covers the paths that could run code. The page still loads Cytoscape,
342
+ > marked, and DOMPurify from a CDN and renders whatever links and diagrams a body
343
+ > contains, so treat an unfamiliar bundle the way you would treat any document
344
+ > from a source you do not know.
345
+
346
+ The [server trust boundary](.okf/design/server-trust-boundary.md) concept has the
347
+ full write-up: the two data paths, what sanitizing does, and what it leaves to
348
+ your judgment.
245
349
 
246
350
  ## Development
247
351
 
data/exe/okf CHANGED
@@ -2,5 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "okf"
5
+ require "okf/skill"
6
+ require "okf/cli"
5
7
 
6
8
  exit OKF::CLI.start(ARGV)
data/lib/okf/cli.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "optparse"
4
+
3
5
  module OKF
4
6
  # Command-line front end: `okf graph|validate|lint|loose|index|catalog|files|tags|types|stats|server <dir>`.
5
7
  # This is the
@@ -11,6 +11,7 @@
11
11
  <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
12
12
  <script src="https://cdn.jsdelivr.net/npm/cytoscape@3.30.2/dist/cytoscape.min.js"></script>
13
13
  <script src="https://cdn.jsdelivr.net/npm/marked@14/marked.min.js"></script>
14
+ <script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"></script>
14
15
  <style>
15
16
  :root{
16
17
  --bg:#ffffff; --panel:#ffffff; --panel-2:#f6f7f8; --canvas:#f4f5f7; --ink:#1f2328; --ink-soft:#333333;
@@ -466,7 +467,7 @@ function rethemeMermaid(){if(!window.mermaid)return;const nodes=[...document.que
466
467
  window.mermaid.initialize({startOnLoad:false,securityLevel:'strict',theme:isDark()?'dark':'default'});
467
468
  nodes.forEach(d=>{if(d.dataset.src){d.removeAttribute('data-processed');d.innerHTML='';d.textContent=d.dataset.src;}});
468
469
  window.mermaid.run({nodes}).catch(()=>{});}
469
- function renderMarkdown(el,text){el.innerHTML=text?marked.parse(text):'<span class="empty">empty body</span>';processMermaid(el);}
470
+ function renderMarkdown(el,text){el.innerHTML=text?DOMPurify.sanitize(marked.parse(text)):'<span class="empty">empty body</span>';processMermaid(el);}
470
471
 
471
472
  /* ── inspector ── */
472
473
  const gbody=document.querySelector('.graph-body'), sideBody=document.getElementById('side-body');
data/lib/okf/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OKF
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
data/lib/okf.rb CHANGED
@@ -4,7 +4,6 @@ require "erb"
4
4
  require "fileutils"
5
5
  require "date"
6
6
  require "json"
7
- require "optparse"
8
7
  require "pathname"
9
8
  require "securerandom"
10
9
  require "set"
@@ -50,6 +49,4 @@ module OKF
50
49
  require "okf/bundle/reader"
51
50
  require "okf/bundle/writer"
52
51
  require "okf/bundle/folder"
53
- require "okf/skill"
54
- require "okf/cli"
55
52
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: okf
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
@@ -38,11 +38,13 @@ dependencies:
38
38
  - !ruby/object:Gem::Version
39
39
  version: '1.4'
40
40
  description: |
41
- OKF is portable knowledge Markdown files with YAML frontmatter that both
42
- humans and agents read. This gem reads OKF bundles, checks them for v0.1 (§9)
43
- conformance, and serves them as an interactive graph (a mountable Rack app).
44
- It ships a library API (OKF::Bundle and friends) plus an `okf` command-line
45
- tool (validate / lint / loose / server / graph / skill).
41
+ OKF (Open Knowledge Format) is portable knowledge: Markdown files with YAML
42
+ frontmatter that both humans and agents read from one source. This gem is the
43
+ Ruby-native way to work with it. Its companion agent skill authors and curates
44
+ a bundle; the `okf` command-line tool validates the result for v0.1 (§9)
45
+ conformance, lints its curation quality, and serves it as an interactive graph
46
+ (a mountable Rack app). The same validate, lint, and graph run in-process
47
+ through a library API (OKF::Bundle and friends).
46
48
  email:
47
49
  - rodrigo.serradura@gmail.com
48
50
  executables:
@@ -138,5 +140,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
140
  requirements: []
139
141
  rubygems_version: 4.0.16
140
142
  specification_version: 4
141
- summary: Read, validate, and serve Open Knowledge Format (OKF) v0.1 bundles.
143
+ summary: 'A lightweight Ruby gem for OKF: author, curate, and serve bundles as an
144
+ interactive graph.'
142
145
  test_files: []