@panaversity/ksor 0.0.39 → 0.0.41
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.
- package/CHANGELOG.md +965 -0
- package/README.md +29 -9
- package/dist/checker/check-main.mjs +14049 -0
- package/dist/cli.mjs +11528 -5213
- package/dist/gateway-api-CF4ED9_g-BQusM_dK.mjs +10895 -0
- package/dist/gateway.d.mts +52 -13
- package/dist/gateway.mjs +2 -2
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-pl4aOpVs.mjs → src-dqpI-p1a.mjs} +1 -0
- package/docs/authorization.md +8 -6
- package/docs/deploying.md +36 -25
- package/docs/index.md +26 -13
- package/docs/ingesting.md +70 -22
- package/docs/tool-surface.md +69 -16
- package/package.json +4 -3
- package/schema/migrations/2.4-2.5__okf-profile.sql +114 -0
- package/schema/schema.sql +77 -14
- package/templates/scaffold/.agents/skills/add-sources/SKILL.md +63 -18
- package/templates/scaffold/.agents/skills/format-checker/SKILL.md +42 -33
- package/templates/scaffold/.agents/skills/format-checker/check.mjs +13827 -1314
- package/templates/scaffold/.agents/skills/intake-interview/SKILL.md +65 -27
- package/templates/scaffold/.agents/skills/make-slides/SKILL.md +7 -5
- package/templates/scaffold/.agents/skills/make-summary/SKILL.md +13 -6
- package/templates/scaffold/.claude/skills/add-sources/SKILL.md +63 -18
- package/templates/scaffold/.claude/skills/format-checker/SKILL.md +42 -33
- package/templates/scaffold/.claude/skills/format-checker/check.mjs +13827 -1314
- package/templates/scaffold/.claude/skills/intake-interview/SKILL.md +65 -27
- package/templates/scaffold/.claude/skills/make-slides/SKILL.md +7 -5
- package/templates/scaffold/.claude/skills/make-summary/SKILL.md +13 -6
- package/templates/scaffold/.github/workflows/validate.yml +9 -1
- package/templates/scaffold/.ksor/governance.yaml +17 -0
- package/templates/scaffold/AGENTS.md +293 -119
- package/templates/scaffold/Dockerfile +5 -1
- package/templates/scaffold/README.md +160 -42
- package/templates/scaffold/env.example +37 -6
- package/templates/scaffold/gitignore +14 -6
- package/templates/scaffold/instance.md +21 -17
- package/templates/scaffold/knowledge/governance-ladder.md +11 -2
- package/templates/scaffold/knowledge/index.md +9 -0
- package/templates/scaffold/knowledge/surfaces/for-agents.md +13 -6
- package/templates/scaffold/knowledge/surfaces/for-people.md +7 -6
- package/templates/scaffold/knowledge/surfaces/index.md +4 -20
- package/templates/scaffold/knowledge/surfaces/overview.md +25 -0
- package/templates/scaffold/knowledge/what-is-a-ksor.md +6 -5
- package/templates/scaffold/knowledge/what-is-a-ksor.summary.md +4 -0
- package/templates/scaffold/package.json +3 -4
- package/templates/scaffold/pnpm-lock.yaml +3 -0
- package/templates/scaffold/system/gateways/content.ts +13 -0
- package/templates/scaffold/system/site/app/(home)/page.tsx +2 -2
- package/templates/scaffold/system/site/app/.well-known/mcp/server.json/route.ts +10 -0
- package/templates/scaffold/system/site/app/docs/[[...slug]]/page.tsx +134 -91
- package/templates/scaffold/system/site/app/global.css +198 -6
- package/templates/scaffold/system/site/app/layout.tsx +8 -3
- package/templates/scaffold/system/site/app/llms-full.txt/route.ts +13 -7
- package/templates/scaffold/system/site/app/llms.txt/route.ts +12 -7
- package/templates/scaffold/system/site/app/md/[[...slug]]/route.ts +26 -25
- package/templates/scaffold/system/site/components/code-block.tsx +87 -0
- package/templates/scaffold/system/site/components/embed.tsx +276 -0
- package/templates/scaffold/system/site/components/footer-mark.tsx +3 -2
- package/templates/scaffold/system/site/components/governance.tsx +219 -87
- package/templates/scaffold/system/site/components/mdx.tsx +10 -2
- package/templates/scaffold/system/site/components/record-index.tsx +5 -5
- package/templates/scaffold/system/site/components/record-stack.tsx +10 -9
- package/templates/scaffold/system/site/components/record-views.tsx +6 -17
- package/templates/scaffold/system/site/components/sidebar-status.tsx +19 -18
- package/templates/scaffold/system/site/lib/alert-rule.ts +214 -0
- package/templates/scaffold/system/site/lib/attachment-rule.ts +6 -1
- package/templates/scaffold/system/site/lib/attachments.ts +0 -28
- package/templates/scaffold/system/site/lib/audience-rule.ts +15 -21
- package/templates/scaffold/system/site/lib/audience.ts +42 -146
- package/templates/scaffold/system/site/lib/embed-rule.ts +255 -0
- package/templates/scaffold/system/site/lib/governance.ts +339 -225
- package/templates/scaffold/system/site/lib/index-routes.ts +125 -0
- package/templates/scaffold/system/site/lib/lifecycle-rule.ts +52 -0
- package/templates/scaffold/system/site/lib/lock.ts +282 -0
- package/templates/scaffold/system/site/lib/order-rule.ts +37 -0
- package/templates/scaffold/system/site/lib/record-href.ts +68 -0
- package/templates/scaffold/system/site/lib/record-link.tsx +26 -0
- package/templates/scaffold/system/site/lib/rules-version.ts +11 -0
- package/templates/scaffold/system/site/lib/shared.ts +67 -104
- package/templates/scaffold/system/site/lib/sim-rule.ts +49 -0
- package/templates/scaffold/system/site/lib/source.ts +256 -186
- package/templates/scaffold/system/site/lib/stage-knowledge.ts +638 -467
- package/templates/scaffold/system/site/lib/stage-manifest.ts +128 -0
- package/templates/scaffold/system/site/package.json +1 -0
- package/templates/scaffold/system/site/record/actor.ts +23 -0
- package/templates/scaffold/system/site/record/check.ts +571 -0
- package/templates/scaffold/system/site/record/citations.ts +312 -0
- package/templates/scaffold/system/site/record/frontmatter.ts +134 -0
- package/templates/scaffold/system/site/record/git-ledger.ts +171 -0
- package/templates/scaffold/system/site/record/hygiene.ts +320 -0
- package/templates/scaffold/system/site/record/index-file.ts +150 -0
- package/templates/scaffold/system/site/record/index.ts +103 -0
- package/templates/scaffold/system/site/record/instance.ts +257 -0
- package/templates/scaffold/system/site/record/instant.ts +43 -0
- package/templates/scaffold/system/site/record/ledger.ts +694 -0
- package/templates/scaffold/system/site/record/load.ts +129 -0
- package/templates/scaffold/system/site/record/lock.ts +306 -0
- package/templates/scaffold/system/site/record/near-miss.ts +37 -0
- package/templates/scaffold/system/site/record/policy.ts +414 -0
- package/templates/scaffold/system/site/record/profile.ts +535 -0
- package/templates/scaffold/system/site/record/refusal.ts +106 -0
- package/templates/scaffold/system/site/record/yaml-file.ts +103 -0
- package/templates/scaffold/system/site/source.config.ts +109 -23
- package/dist/gateway-api-CmIthmJS-IUA9qS-T.mjs +0 -3225
- package/templates/scaffold/system/site/lib/denial-rule.ts +0 -220
- package/templates/scaffold/system/site/lib/page-order.ts +0 -93
|
@@ -26,6 +26,46 @@ The first `pnpm install` also fetches the
|
|
|
26
26
|
`ksor` tool (pinned in `package.json`) and writes it into your lockfile —
|
|
27
27
|
commit the updated lockfile.
|
|
28
28
|
|
|
29
|
+
**The five starter documents publish on the first build.** They ship
|
|
30
|
+
`status: stable`, so `pnpm dev` and `pnpm build` both give you a working
|
|
31
|
+
record straight away — pages, a sidebar, a `/llms.txt` an agent can read —
|
|
32
|
+
instead of an empty shelf. They are approved by `ksor-starter/KSOR-STAMP-VERSION`:
|
|
33
|
+
the tool that wrote them, named as a producer rather than as a person, because
|
|
34
|
+
no person reviewed a word of it. That is what the trust tier _unverified_ on
|
|
35
|
+
every one of those pages says, and it is true.
|
|
36
|
+
|
|
37
|
+
**So your first act here is replacing them.** They describe KSoR, not your
|
|
38
|
+
organisation, and a record that describes the wrong thing describes it on every
|
|
39
|
+
surface. Delete each one as your own knowledge arrives — and when the last is
|
|
40
|
+
gone, delete `ksor-starter/KSOR-STAMP-VERSION` from `approval_authorities` in
|
|
41
|
+
`.ksor/governance.yaml` too. Nothing of yours should be approved by a tool. Ask
|
|
42
|
+
your coding agent to run the intake interview: it replaces the `human:you`
|
|
43
|
+
placeholder in that file with your real handle and writes `instance.md` with
|
|
44
|
+
you.
|
|
45
|
+
|
|
46
|
+
**What you write starts unpublished.** A new document is `status: draft`, and
|
|
47
|
+
`pnpm build` admits a draft to no surface at all: no page, no sidebar row, no
|
|
48
|
+
`/llms.txt` entry, nothing for an agent to read. `pnpm dev` shows it, marked —
|
|
49
|
+
the preview is where drafts live.
|
|
50
|
+
|
|
51
|
+
Publishing one adds two keys beside `status: stable` — what produced the text,
|
|
52
|
+
and who approved it. Both, or `pnpm check` refuses the document:
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
status: stable
|
|
56
|
+
generated: { by: "human:you", at: 2026-01-31T09:00:00Z }
|
|
57
|
+
ksor:
|
|
58
|
+
audience: [public] # already there — every document carries it, drafts too
|
|
59
|
+
approval: { by: "human:you", at: 2026-01-31T09:00:00Z }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`generated` is provenance: it names whatever produced the text — a person, or
|
|
63
|
+
the agent that drafted it — and nothing has to authorise it. `approval.by` is
|
|
64
|
+
authority, so it must name an actor `.ksor/governance.yaml` lists, and its `at`
|
|
65
|
+
may not be earlier than `generated.at` — the text that was approved has to be
|
|
66
|
+
the text that was written. That act is yours, so the record never claims
|
|
67
|
+
authority nobody granted.
|
|
68
|
+
|
|
29
69
|
### Presenting a document
|
|
30
70
|
|
|
31
71
|
Ask your coding agent for slides and it writes them, from the document, into
|
|
@@ -77,26 +117,44 @@ opening, and the skill will say so rather than write one.
|
|
|
77
117
|
The record's other surface is an MCP server for AI agents — the same
|
|
78
118
|
knowledge, cited, with honest abstention. It is the climbed rung: it needs a
|
|
79
119
|
Postgres store (with pgvector) and an embedding provider key, so it is not
|
|
80
|
-
part of `pnpm dev`.
|
|
120
|
+
part of `pnpm dev`. Three steps, and the order is load-bearing: the command
|
|
121
|
+
block is last because it needs both of the things above it. Skip ahead to it
|
|
122
|
+
and `pnpm provision` refuses, naming the config step 1 writes.
|
|
81
123
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
pnpm provision # once: apply the schema, authorize ingest
|
|
85
|
-
pnpm refresh # ingest the record, collect retired generations
|
|
86
|
-
pnpm serve # the MCP server
|
|
87
|
-
```
|
|
124
|
+
**1. Uncomment the `database:` block already in `instance.md`.** It names the
|
|
125
|
+
VARIABLE holding your DSN, never the DSN itself:
|
|
88
126
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
127
|
+
```yaml
|
|
128
|
+
database:
|
|
129
|
+
dsn_env: KSOR_DB_URL
|
|
130
|
+
```
|
|
92
131
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
config:
|
|
96
|
-
`embedding:` already defaults to Gemini at 1536 dimensions, and leaving
|
|
132
|
+
Nothing below works until it is there — `pnpm provision` refuses with
|
|
133
|
+
`instance.md declares no database: block`. That is also the whole required
|
|
134
|
+
config: `embedding:` already defaults to Gemini at 1536 dimensions, and leaving
|
|
97
135
|
`retrieval:` out starts you with the abstention gate off and honest about it
|
|
98
136
|
(turn it on afterwards with `ksor calibrate`, once the record is serving).
|
|
99
137
|
|
|
138
|
+
**2. Copy the environment file**, then fill in `KSOR_DB_URL`, `GEMINI_API_KEY`
|
|
139
|
+
and `KSOR_AUTH=disabled-local`:
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
cp .env.example .env
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`ksor` reads `.env` automatically — there is nothing to export, and where a
|
|
146
|
+
refusal tells you to _export_ that variable, putting it in `.env` is the same
|
|
147
|
+
thing. `KSOR_AUTH=disabled-local` is required for a local run: serve refuses to
|
|
148
|
+
boot unauthenticated on purpose, so a server is never open by accident.
|
|
149
|
+
|
|
150
|
+
**3. Bring it up.**
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
pnpm provision # once: apply the schema, authorize ingest
|
|
154
|
+
pnpm refresh # build, ingest the record, collect retired generations
|
|
155
|
+
pnpm serve # the MCP server
|
|
156
|
+
```
|
|
157
|
+
|
|
100
158
|
`pnpm provision` runs once — it applies the schema (or migrates it forward) and
|
|
101
159
|
authorizes ingest, the two privileged acts that should not happen on every
|
|
102
160
|
boot. After that: `pnpm refresh` publishes what you have edited, and `pnpm serve`
|
|
@@ -104,10 +162,9 @@ runs the server. They are separate because publishing is an act, not a side
|
|
|
104
162
|
effect of starting a process. A rerun on an unchanged record
|
|
105
163
|
costs nothing: no new generation, no embedding, no rows. Edit a document and
|
|
106
164
|
the next run picks up exactly that change. `AGENTS.md` → "Serving to agents" is the
|
|
107
|
-
full runbook; your coding agent reads it first.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
a public bind needs a configured SSO door instead. Any other operation is
|
|
165
|
+
full runbook; your coding agent reads it first. A public bind needs a
|
|
166
|
+
configured SSO door rather than `disabled-local` — see step 2 and
|
|
167
|
+
"The agent surface deploys separately" below. Any other operation is
|
|
111
168
|
`pnpm exec ksor <verb>`.
|
|
112
169
|
|
|
113
170
|
### Test the agent surface with an actual agent
|
|
@@ -194,6 +251,40 @@ equivalent (its default refusal of dependency install scripts covers the
|
|
|
194
251
|
OTHER half of that posture), and this sentence is the disclosure.
|
|
195
252
|
<!-- /ksor:pm -->
|
|
196
253
|
|
|
254
|
+
### A note on `audit`
|
|
255
|
+
|
|
256
|
+
An audit of this scaffold reports vulnerabilities in `next`, and will keep
|
|
257
|
+
doing so: a framework that large always has open advisories against whatever
|
|
258
|
+
version you have pinned.
|
|
259
|
+
<!-- ksor:pm npm -->
|
|
260
|
+
`npm install` prints the count at the end of every install, so you meet it
|
|
261
|
+
before you have run anything, next to an invitation to run
|
|
262
|
+
`npm audit fix --force`.
|
|
263
|
+
<!-- /ksor:pm -->
|
|
264
|
+
<!-- ksor:pm pnpm -->
|
|
265
|
+
pnpm reports it only when you run `pnpm audit`.
|
|
266
|
+
<!-- /ksor:pm -->
|
|
267
|
+
<!-- ksor:pm bun -->
|
|
268
|
+
bun reports it only when you run `bun audit`.
|
|
269
|
+
<!-- /ksor:pm -->
|
|
270
|
+
|
|
271
|
+
**Never let an audit tool raise the pin for you.** It moves off the version
|
|
272
|
+
this scaffold was built and tested against, and that pin is the whole reason
|
|
273
|
+
two machines produce the same site. Bump it deliberately instead — take the
|
|
274
|
+
newer pin a newer `ksor init` emits, or raise it yourself and re-run
|
|
275
|
+
`pnpm build` and the deploy check above.
|
|
276
|
+
|
|
277
|
+
It also reads worse than it is, for one structural reason worth knowing:
|
|
278
|
+
**this site is a static export.** `pnpm build` writes HTML, JS and CSS to
|
|
279
|
+
`system/site/out/`, and no framework server ever runs in front of your
|
|
280
|
+
readers — no middleware, no server actions, no rewrites, no image optimizer.
|
|
281
|
+
Most framework advisories describe exactly those request paths, so they have
|
|
282
|
+
nothing here to reach. Two things that argument does NOT cover, and you should
|
|
283
|
+
treat as real: an advisory in the **build** toolchain, which does run, on your
|
|
284
|
+
machine and in your CI; and any advisory at all if you later add a served route
|
|
285
|
+
and stop exporting. Read what an advisory affects before deciding it is inert —
|
|
286
|
+
the static export is a reason, not a blanket.
|
|
287
|
+
|
|
197
288
|
## The files, explained
|
|
198
289
|
|
|
199
290
|
Nothing here is decoration, and the dotfiles are not ceremony — each one is a
|
|
@@ -203,7 +294,12 @@ different coding agent's way of finding the same working contract.
|
|
|
203
294
|
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
204
295
|
| `knowledge/` | **the record** — your governed markdown. The product; everything else serves it. |
|
|
205
296
|
| `system/` | the code that serves the record: the site today, more as you need it. |
|
|
206
|
-
| `instance.md` | what this record is authoritative for; its `name:` is the identity every surface publishes (read at server/build start — restart `pnpm dev` after
|
|
297
|
+
| `instance.md` | what this record is authoritative for; its `name:` is the identity every surface publishes and its `title:` the display title every page leads with (both read at server/build start — restart `pnpm dev` after changing either). Its BODY is the agent surface's system prompt — `ksor serve` wires it into the MCP server's instructions. |
|
|
298
|
+
| `.ksor/governance.yaml` | **the root of authority** — which audiences exist, who may approve a document, who may take one down. Committed; every governance act is checked against it. |
|
|
299
|
+
| `.ksor/takedowns.yaml` | the takedown ledger: every withdrawal and every lift, append-only and committed, so the site honours a takedown with no database in the loop. It appears at your first `ksor takedown` — an empty ledger would assert an act nobody performed. |
|
|
300
|
+
| `build.lock.json` | what the last `ksor build` published — the corpus, the commit, the toolchain — and what every machine surface stamps. Committed; written by `ksor build`, never by hand. |
|
|
301
|
+
| `Dockerfile`, `.dockerignore` | how the agent surface reaches a host. The Dockerfile names no host; `vercel.json` points at it rather than replacing it, so moving hosts is a redeploy. |
|
|
302
|
+
| `vercel.json` | one domain, two services — the static site and the MCP door — for the host this scaffold answers the setup interview for. Delete it if you deploy elsewhere. |
|
|
207
303
|
| `AGENTS.md` | the working contract every coding agent reads first — the rules for writing knowledge here. |
|
|
208
304
|
| `CLAUDE.md` | one line, pointing at `AGENTS.md`. Claude Code looks for this filename, not that one. |
|
|
209
305
|
| `.agents/skills/` | the agent kit: `intake-interview` (define the record with you), `add-sources` (turn source material into governed documents), `make-slides` (generate a presentation from a document and attach it), `make-summary` (write a document's summary and attach it), `format-checker` (the rules, as a program). |
|
|
@@ -212,7 +308,7 @@ different coding agent's way of finding the same working contract.
|
|
|
212
308
|
| `.github/workflows/validate.yml` | your CI: runs the same checker on every pull request and push to main. |
|
|
213
309
|
| `.gitattributes` | markdown is checked out byte-stable on every platform, so the same commit hashes the same everywhere. |
|
|
214
310
|
| `.env.example` | the variables the served rung needs; copy to `.env` (gitignored) and fill in. |
|
|
215
|
-
| `.gitignore` | keeps build output, `node_modules/`, and `.env` out of the record's history.
|
|
311
|
+
| `.gitignore` | keeps build output, `node_modules/`, and `.env` out of the record's history — and negates two paths inside `.ksor/`, because the policy and the ledger ARE the record. |
|
|
216
312
|
| `package.json` | the surface commands — `pnpm dev` (the site) and `pnpm provision` / `pnpm refresh` / `pnpm serve` (the agent surface: set up once, publish, then serve) — plus `pnpm build` / `pnpm check`, the pinned `@panaversity/ksor` tool and the workspace layout the manifest declares. |
|
|
217
313
|
<!-- ksor:pm pnpm -->
|
|
218
314
|
| `pnpm-workspace.yaml` | where the workspace looks for code (`system/site`, plus reserved `system/gateways/*` and `system/packages/*`), and the supply-chain policy for installs. |
|
|
@@ -250,27 +346,37 @@ and anything that can serve files can serve it.
|
|
|
250
346
|
If the build image's pnpm predates the `packageManager` pin, set the
|
|
251
347
|
`ENABLE_EXPERIMENTAL_COREPACK=1` build environment variable.
|
|
252
348
|
<!-- /ksor:pm -->
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
349
|
+
**`pnpm build` runs `ksor build` first.** It generates every `index.md`,
|
|
350
|
+
runs the record checker, and writes `build.lock.json` — the committed record
|
|
351
|
+
of what was published, from which commit, with which toolchain — and only
|
|
352
|
+
then builds the site. A checker refusal stops the build before anything is
|
|
353
|
+
written. Takedowns reach the site through `.ksor/takedowns.yaml`, the
|
|
354
|
+
committed ledger — a file in this repository, so the site build needs no
|
|
355
|
+
database access at all.
|
|
356
|
+
|
|
357
|
+
That is deliberate. The act that withdraws a document is one merged commit,
|
|
358
|
+
and both surfaces read it: the door refuses immediately, the site at its next
|
|
359
|
+
build. Merge the ledger entry, rebuild, redeploy.
|
|
360
|
+
|
|
361
|
+
**A withdrawal that arrives on a clock works the same way, and that one has to
|
|
362
|
+
be scheduled.** `stale_after` and `ksor.effective_from` are evaluated once per
|
|
363
|
+
build, at the instant that build ran, and the answer is written into
|
|
364
|
+
`system/site/out/` — static files cannot re-decide themselves. So a document
|
|
365
|
+
whose `stale_after` passes after your last build keeps appearing in `/llms.txt`
|
|
366
|
+
and in its markdown twin, while `ksor serve` — a process, evaluating per
|
|
367
|
+
request — already refuses it. `ksor build` prints the next instant at which
|
|
368
|
+
this happens. Nothing here rebuilds for you: `validate.yml` runs on pull
|
|
369
|
+
requests and `vercel.json` declares no cron. If this record uses either key,
|
|
370
|
+
add a scheduled rebuild and redeploy.
|
|
268
371
|
|
|
269
372
|
- **GitHub Pages, nginx, S3, anything static** — run `pnpm build` and
|
|
270
373
|
upload `system/site/out/`. Hosted under a sub-path (like
|
|
271
374
|
`user.github.io/repo`)? Build with `KSOR_BASE_PATH=/repo pnpm build`.
|
|
272
|
-
- **Verify any deploy** the same way: the home page, one document page
|
|
273
|
-
|
|
375
|
+
- **Verify any deploy** the same way: the home page, one document page and
|
|
376
|
+
`/llms.txt` load, and each names the documents this record has approved. On a
|
|
377
|
+
record whose documents are all still drafts, the home page and `/llms.txt`
|
|
378
|
+
come up empty and there is no document page at all — which is the correct
|
|
379
|
+
answer, not a broken deploy. Approve a document and rebuild to see it change.
|
|
274
380
|
|
|
275
381
|
### The agent surface deploys separately
|
|
276
382
|
|
|
@@ -280,9 +386,20 @@ Cloud Run, Fly, Render, ECS, Kubernetes or a VPS:
|
|
|
280
386
|
|
|
281
387
|
```sh
|
|
282
388
|
docker build -t my-record .
|
|
283
|
-
docker run --rm -p 8080:80 --env-file .env
|
|
389
|
+
docker run --rm -p 8080:80 --env-file .env \
|
|
390
|
+
-e KSOR_AUTH=disabled-public my-record
|
|
284
391
|
```
|
|
285
392
|
|
|
393
|
+
**That last flag is not boilerplate, and it is not a workaround.** The image
|
|
394
|
+
sets `$PORT`, so the door binds `0.0.0.0` — a PUBLIC bind — and the
|
|
395
|
+
`KSOR_AUTH=disabled-local` your `.env` carries refuses there by design, saying
|
|
396
|
+
so in as many words. Your laptop is not the exception: a container really is
|
|
397
|
+
reachable from outside itself, and `disabled-public` is you saying you know
|
|
398
|
+
that. It goes on the command rather than into `.env` so your ordinary
|
|
399
|
+
`pnpm serve` keeps the loopback posture — and a real deployment sets it (or,
|
|
400
|
+
better, the SSO variables) in the host's environment, since `.dockerignore`
|
|
401
|
+
keeps `.env` out of the image entirely.
|
|
402
|
+
|
|
286
403
|
One thing surprises people: **deploying does not publish.** The door serves
|
|
287
404
|
whatever generation is already in the database, so a first deploy with no
|
|
288
405
|
`pnpm refresh` serves an empty record. Publishing is a step you run — from your
|
|
@@ -290,10 +407,11 @@ machine or from CI — and it is deliberately not something a booting container
|
|
|
290
407
|
does. The full walkthrough, including what a cold start costs and where ingest
|
|
291
408
|
belongs, is in `node_modules/@panaversity/ksor/docs/deploying.md`.
|
|
292
409
|
|
|
293
|
-
If
|
|
294
|
-
Plain `pnpm build`
|
|
295
|
-
`KSOR_AUDIENCE
|
|
296
|
-
|
|
410
|
+
If `.ksor/governance.yaml` registers audiences, what you deploy is a
|
|
411
|
+
**viewer**. Plain `pnpm build` builds for `[public]` — safe for any host.
|
|
412
|
+
`KSOR_AUDIENCE=public,<audience> pnpm build` — a comma list that must always
|
|
413
|
+
include `public` — builds for a wider viewer's own deployment, and that build
|
|
414
|
+
carries an
|
|
297
415
|
"— not for publication" label because it must never reach a public host:
|
|
298
416
|
put it behind access control you already trust (VPN, SSO proxy,
|
|
299
417
|
authenticated host). The tiers govern what a build contains; where each
|
|
@@ -26,7 +26,10 @@ GEMINI_API_KEY=
|
|
|
26
26
|
# a deploy go green.
|
|
27
27
|
#
|
|
28
28
|
# A container sets $PORT, so the door binds 0.0.0.0 — that is a public bind, and
|
|
29
|
-
# `disabled-local` will refuse there. That refusal is the point.
|
|
29
|
+
# `disabled-local` will refuse there. That refusal is the point. To smoke-test
|
|
30
|
+
# the image locally, acknowledge it on the command instead of changing the line
|
|
31
|
+
# below, so a plain `ksor serve` keeps the loopback posture:
|
|
32
|
+
# docker run --rm -p 8080:80 --env-file .env -e KSOR_AUTH=disabled-public my-record
|
|
30
33
|
#
|
|
31
34
|
# For a real deployment, delete this line and configure the SSO door instead:
|
|
32
35
|
# KSOR_SSO_URL=https://your-sso.example.com
|
|
@@ -48,7 +51,7 @@ KSOR_AUTH=disabled-local
|
|
|
48
51
|
# "Keeping people out of the site".
|
|
49
52
|
#
|
|
50
53
|
# All three are required together, and are inlined at BUILD time — set them
|
|
51
|
-
# before
|
|
54
|
+
# before the site build, not on the running site. Leave them unset and the
|
|
52
55
|
# sign-in control does not render at all.
|
|
53
56
|
#
|
|
54
57
|
# The issuer is the same authorization server the door names in KSOR_SSO_URL.
|
|
@@ -76,10 +79,38 @@ KSOR_AUTH=disabled-local
|
|
|
76
79
|
# validating after it, even with one instance.
|
|
77
80
|
# KSOR_SNAPSHOT_KEYS=v1=<32+ random bytes>
|
|
78
81
|
|
|
79
|
-
#
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
82
|
+
# WHO this surface answers as: a comma list of audience identifiers, validated
|
|
83
|
+
# against the registry in .ksor/governance.yaml. It must always contain
|
|
84
|
+
# `public`, and unset means exactly `[public]`. A document is visible when its
|
|
85
|
+
# `ksor.audience` list and this one OVERLAP — there is no ranking, so
|
|
86
|
+
# `public,internal` is the reader who may see both, not a level.
|
|
87
|
+
# The same variable the site's per-audience build reads, so both surfaces mean
|
|
88
|
+
# the same thing. A build for anything wider than `public` belongs behind that
|
|
89
|
+
# audience's own access control, never on a public host.
|
|
90
|
+
# KSOR_AUDIENCE=public,internal
|
|
91
|
+
|
|
92
|
+
# The LOWEST trust tier this door will answer from: unverified (the default),
|
|
93
|
+
# machine-confirmed, or human-reviewed. unverified is the honest state of a
|
|
94
|
+
# record nobody has reviewed, and it is not a defect.
|
|
95
|
+
# Know what the tier IS before you raise the floor onto it. The tier itself is
|
|
96
|
+
# derived and may not be authored (`trust_tier:` in frontmatter is refused),
|
|
97
|
+
# but its input is: a document's own `verified:` entries. Nothing in
|
|
98
|
+
# `.ksor/governance.yaml` gates who may appear there — the policy authorises
|
|
99
|
+
# `ksor.approval` and takedown, and has no verification family — so what stands
|
|
100
|
+
# behind `human-reviewed` is review of the pull request that added the entry,
|
|
101
|
+
# and nothing else. A document that declares a review of itself gets the tier.
|
|
102
|
+
# A caller can raise this per call with the `min_trust_tier` argument; it can
|
|
103
|
+
# never be lowered below what is set here. Setting `human-reviewed` narrows the
|
|
104
|
+
# door to the documents that CLAIM a human review: on a record with no reviews
|
|
105
|
+
# that is nothing at all, and on a record where one document declares its own
|
|
106
|
+
# it is that document ALONE — a floor can invert if you raise it without
|
|
107
|
+
# reading what it admits.
|
|
108
|
+
# KSOR_MIN_TRUST_TIER=unverified
|
|
109
|
+
|
|
110
|
+
# `ksor build` records drafts as hidden from every surface (the default) or
|
|
111
|
+
# shown on HUMAN surfaces only — a review build, marked noindex. Recorded in
|
|
112
|
+
# build.lock.json and in build_id; never set it for a build you deploy.
|
|
113
|
+
# KSOR_DRAFTS=show
|
|
83
114
|
|
|
84
115
|
# Bind. PORT is honoured too, for hosts that inject it.
|
|
85
116
|
# KSOR_MCP_HOST=127.0.0.1
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# ksor's working directory — build output and scratch, never the record.
|
|
2
|
+
# The two governance files inside it ARE the record (the policy and the
|
|
3
|
+
# takedown ledger) and are un-ignored by name: the directory form `.ksor/`
|
|
4
|
+
# cannot be negated, so the glob is `.ksor/*`.
|
|
5
|
+
.ksor/*
|
|
6
|
+
!.ksor/governance.yaml
|
|
7
|
+
!.ksor/takedowns.yaml
|
|
3
8
|
|
|
4
9
|
node_modules/
|
|
5
10
|
system/site/.next/
|
|
@@ -8,9 +13,16 @@ system/site/out/
|
|
|
8
13
|
# the per-audience copy of the record a build stages — a filtered derivative,
|
|
9
14
|
# never a second record; committing it would publish what a build excluded
|
|
10
15
|
system/site/.staged-knowledge/
|
|
16
|
+
# and its manifest, which names this build's viewer and every concept it
|
|
17
|
+
# staged — committing it publishes the restricted viewer's page list
|
|
18
|
+
system/site/.staged-knowledge.json
|
|
11
19
|
# and the lock that keeps one evaluation of a build staging at a time; it only
|
|
12
20
|
# outlives a build that was killed mid-stage, and the next build clears it
|
|
13
21
|
system/site/.staged-knowledge.lock
|
|
22
|
+
# where this build SERVES the sims it published — copied out of the staged
|
|
23
|
+
# record, so it holds one viewer's sims and the next build prunes the rest;
|
|
24
|
+
# the record owns the sim, this is only the copy that gets a url
|
|
25
|
+
system/site/public/sims/
|
|
14
26
|
*.tsbuildinfo
|
|
15
27
|
|
|
16
28
|
# secrets never enter the record — system/ is their future home (serve)
|
|
@@ -18,7 +30,3 @@ system/site/.staged-knowledge.lock
|
|
|
18
30
|
!.env.example
|
|
19
31
|
|
|
20
32
|
.DS_Store
|
|
21
|
-
|
|
22
|
-
# The takedown manifest the site build reads — exported from the database,
|
|
23
|
-
# never hand-edited, and re-exported on every build.
|
|
24
|
-
.ksor-denylist.json
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
format:
|
|
2
|
+
format: 2
|
|
3
3
|
name: KSOR-STAMP-NAME
|
|
4
|
-
|
|
4
|
+
title: KSoR
|
|
5
|
+
description: What a Knowledge System of Record is, how a project climbs the governance ladder, and which surfaces publish the same governed knowledge.
|
|
6
|
+
toolchain:
|
|
5
7
|
requires: ">=KSOR-STAMP-VERSION"
|
|
6
8
|
scaffolded: "KSOR-STAMP-VERSION"
|
|
7
9
|
# The served MCP rung needs ONE required block: the NAME of the environment
|
|
@@ -23,8 +25,6 @@ ksor:
|
|
|
23
25
|
# version: 0.1.0
|
|
24
26
|
---
|
|
25
27
|
|
|
26
|
-
# KSoR
|
|
27
|
-
|
|
28
28
|
This record is authoritative for what a Knowledge System of Record is, how a
|
|
29
29
|
project climbs the governance ladder, and which surfaces the same governed
|
|
30
30
|
knowledge is published through. It does not cover the CLI's release history or
|
|
@@ -37,23 +37,27 @@ record wins.
|
|
|
37
37
|
## This is a starter, and it is yours to replace
|
|
38
38
|
|
|
39
39
|
Everything above describes KSoR itself. It ships filled in so that a fresh
|
|
40
|
-
project has a real governed corpus on the first `pnpm dev` —
|
|
41
|
-
|
|
40
|
+
project has a real governed corpus on the first `pnpm dev` — five approved
|
|
41
|
+
documents, three of them inside a folder, and one carrying all four study
|
|
42
|
+
attachments (a summary, slides, flashcards and a quiz) — instead of an empty
|
|
43
|
+
shelf and a placeholder.
|
|
42
44
|
The documents live in `knowledge/`; delete them as your own knowledge arrives.
|
|
43
45
|
|
|
44
46
|
Be deliberate about replacing it, because a starter that describes the wrong
|
|
45
|
-
thing describes it _everywhere_.
|
|
46
|
-
|
|
47
|
-
-
|
|
48
|
-
|
|
49
|
-
what citations and `llms.txt` use.
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
thing describes it _everywhere_. Three things here are read by every surface:
|
|
48
|
+
|
|
49
|
+
- **`title:`** is the display title — the human name every page leads with and
|
|
50
|
+
the heading of the record's root index. The machine identity stays
|
|
51
|
+
`KSOR-STAMP-NAME` in `name:`, and that is what citations and `llms.txt` use.
|
|
52
|
+
- **`description:`** is one sentence that seeds `llms.txt` and the MCP
|
|
53
|
+
discovery document.
|
|
54
|
+
- **This body** is the MCP server's instructions, handed in full to every
|
|
55
|
+
connecting agent; its first paragraph is this record's scope, which the site
|
|
56
|
+
publishes. A record published with it unchanged will tell an agent — quite
|
|
57
|
+
accurately, and quite uselessly for you — that it is authoritative for what
|
|
58
|
+
a Knowledge System of Record is.
|
|
55
59
|
|
|
56
60
|
Ask your coding agent to run the **intake interview** (it knows how — see
|
|
57
61
|
`.agents/skills/intake-interview/`), answer its questions, and let it write
|
|
58
|
-
this document with you. Replace those
|
|
62
|
+
this document with you. Replace those three and every surface follows,
|
|
59
63
|
because every surface reads them from here.
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
+
type: Document
|
|
2
3
|
title: The governance ladder
|
|
3
4
|
description: Level 0 works immediately; a project climbs only as far as its domain needs.
|
|
4
|
-
status:
|
|
5
|
-
owner: Product
|
|
5
|
+
status: stable
|
|
6
6
|
order: 3
|
|
7
|
+
generated: { by: "ksor-starter/KSOR-STAMP-VERSION", at: 2026-08-25T00:00:00Z }
|
|
8
|
+
ksor:
|
|
9
|
+
audience: [public]
|
|
10
|
+
approval: { by: "ksor-starter/KSOR-STAMP-VERSION", at: 2026-08-25T00:00:00Z }
|
|
7
11
|
---
|
|
8
12
|
|
|
9
13
|
Governance here is a ladder, not a gate. Demanding the top rung of a project on
|
|
@@ -18,6 +22,11 @@ record of such documents is already publishable, searchable and citable.
|
|
|
18
22
|
|
|
19
23
|
Each rung is worth climbing when the domain asks for it, and not before.
|
|
20
24
|
|
|
25
|
+
> [!TIP]
|
|
26
|
+
> Climb one rung at a time, and only when something has gone wrong without it.
|
|
27
|
+
> A record that adds owners before anyone disputes a document spends review
|
|
28
|
+
> effort buying nothing.
|
|
29
|
+
|
|
21
30
|
### Owners and sources
|
|
22
31
|
|
|
23
32
|
An owner names who stands behind a document. Provenance names where its claims
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
okf_version: "0.2"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# KSoR
|
|
6
|
+
|
|
7
|
+
* [What a Knowledge System of Record is](what-is-a-ksor.md) - The authoritative, governed knowledge layer humans, agents and software operate from.
|
|
8
|
+
* [The governance ladder](governance-ladder.md) - Level 0 works immediately; a project climbs only as far as its domain needs.
|
|
9
|
+
* [Surfaces](surfaces/)
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
+
type: Document
|
|
2
3
|
title: The agent surface
|
|
3
4
|
description: MCP for retrieval with citations, and machine-readable files beside it.
|
|
4
|
-
status:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
status: stable
|
|
6
|
+
order: 6
|
|
7
|
+
generated: { by: "ksor-starter/KSOR-STAMP-VERSION", at: 2026-08-25T00:00:00Z }
|
|
8
|
+
ksor:
|
|
9
|
+
audience: [public]
|
|
10
|
+
approval: { by: "ksor-starter/KSOR-STAMP-VERSION", at: 2026-08-25T00:00:00Z }
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
Agents reach the record through MCP — an open standard, so one corpus answers in
|
|
@@ -22,6 +23,12 @@ be checked against the document that carries it rather than taken on trust.
|
|
|
22
23
|
"Not in this record" is a correct answer. It is never an error, and never a
|
|
23
24
|
licence to fall back on what a model happens to remember.
|
|
24
25
|
|
|
26
|
+
> [!WARNING]
|
|
27
|
+
>
|
|
28
|
+
> An agent that fills a gap from its own memory has not used this record — it
|
|
29
|
+
> has used it as an opening paragraph. Nothing in the answer says which half
|
|
30
|
+
> came from where.
|
|
31
|
+
|
|
25
32
|
## Files beside the interface
|
|
26
33
|
|
|
27
34
|
The build publishes the same knowledge as plain files an agent can fetch without
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
+
type: Document
|
|
2
3
|
title: The human surface
|
|
3
4
|
description: Pages for reading, reviewing and sharing the record.
|
|
4
|
-
status:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
status: stable
|
|
6
|
+
order: 5
|
|
7
|
+
generated: { by: "ksor-starter/KSOR-STAMP-VERSION", at: 2026-08-25T00:00:00Z }
|
|
8
|
+
ksor:
|
|
9
|
+
audience: [public]
|
|
10
|
+
approval: { by: "ksor-starter/KSOR-STAMP-VERSION", at: 2026-08-25T00:00:00Z }
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
A static site renders every governed document at a route derived from its path,
|
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
title: Surfaces
|
|
3
|
-
description: One source, published through several synchronized projections.
|
|
4
|
-
status: approved
|
|
5
|
-
owner: Product
|
|
6
|
-
order: 2
|
|
7
|
-
---
|
|
1
|
+
# Surfaces
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
## Why they cannot drift
|
|
13
|
-
|
|
14
|
-
A surface is generated from the record at build time. Adding one never means
|
|
15
|
-
editing the knowledge, and a document changed in one place changes everywhere
|
|
16
|
-
the next time the record is published.
|
|
17
|
-
|
|
18
|
-
## The two audiences
|
|
19
|
-
|
|
20
|
-
A person reads pages. An agent reads bytes. Both are served from the same
|
|
21
|
-
governed markdown, which is what makes them answer alike.
|
|
3
|
+
* [Surfaces](overview.md) - One source, published through several synchronized projections.
|
|
4
|
+
* [The human surface](for-people.md) - Pages for reading, reviewing and sharing the record.
|
|
5
|
+
* [The agent surface](for-agents.md) - MCP for retrieval with citations, and machine-readable files beside it.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Document
|
|
3
|
+
title: Surfaces
|
|
4
|
+
description: One source, published through several synchronized projections.
|
|
5
|
+
status: stable
|
|
6
|
+
order: 4
|
|
7
|
+
generated: { by: "ksor-starter/KSOR-STAMP-VERSION", at: 2026-08-25T00:00:00Z }
|
|
8
|
+
ksor:
|
|
9
|
+
audience: [public]
|
|
10
|
+
approval: { by: "ksor-starter/KSOR-STAMP-VERSION", at: 2026-08-25T00:00:00Z }
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
The record is written once and published through surfaces that cannot disagree,
|
|
14
|
+
because each derives from the same source rather than from a copy of it.
|
|
15
|
+
|
|
16
|
+
## Why they cannot drift
|
|
17
|
+
|
|
18
|
+
A surface is generated from the record at build time. Adding one never means
|
|
19
|
+
editing the knowledge, and a document changed in one place changes everywhere
|
|
20
|
+
the next time the record is published.
|
|
21
|
+
|
|
22
|
+
## The two audiences
|
|
23
|
+
|
|
24
|
+
A person reads pages. An agent reads bytes. Both are served from the same
|
|
25
|
+
governed markdown, which is what makes them answer alike.
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
+
type: Document
|
|
2
3
|
title: What a Knowledge System of Record is
|
|
3
4
|
description: The authoritative, governed knowledge layer humans, agents and software operate from.
|
|
4
|
-
status:
|
|
5
|
-
owner: Product
|
|
5
|
+
status: stable
|
|
6
6
|
order: 1
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
generated: { by: "ksor-starter/KSOR-STAMP-VERSION", at: 2026-08-25T00:00:00Z }
|
|
8
|
+
ksor:
|
|
9
|
+
audience: [public]
|
|
10
|
+
approval: { by: "ksor-starter/KSOR-STAMP-VERSION", at: 2026-08-25T00:00:00Z }
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
A traditional system of record establishes what is true about the current state
|
|
@@ -5,15 +5,14 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "pnpm -C system/site dev",
|
|
8
|
-
"build": "
|
|
9
|
-
"export-denylist": "ksor takedown --instance instance.md --export .ksor-denylist.json",
|
|
8
|
+
"build": "ksor build && pnpm -C system/site build",
|
|
10
9
|
"check": "node .agents/skills/format-checker/check.mjs",
|
|
11
10
|
"provision": "pnpm schema && pnpm grant",
|
|
12
11
|
"serve": "ksor serve",
|
|
13
|
-
"refresh": "pnpm ingest && pnpm gc",
|
|
12
|
+
"refresh": "ksor build && pnpm ingest && pnpm gc",
|
|
14
13
|
"schema": "ksor schema --instance instance.md --apply",
|
|
15
14
|
"grant": "ksor grant --instance instance.md",
|
|
16
|
-
"ingest": "ksor ingest --instance instance.md --
|
|
15
|
+
"ingest": "ksor ingest --instance instance.md --flip",
|
|
17
16
|
"gc": "ksor gc --instance instance.md"
|
|
18
17
|
},
|
|
19
18
|
"dependencies": {
|