@panaversity/ksor 0.0.3 → 0.0.5

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.
@@ -2,15 +2,15 @@
2
2
  name: intake-interview
3
3
  description: The first conversation with the owner of this Knowledge System of Record — six questions that define what it is authoritative for and who may read it, then write instance.md together. Use when the owner asks to set up, configure, or "get started with" this project, when instance.md still contains its scaffold placeholder text, or when the scope of the corpus is unclear.
4
4
  metadata:
5
- version: "1.2.0"
5
+ version: "1.3.0"
6
6
  ---
7
7
 
8
8
  # Intake interview
9
9
 
10
10
  `instance.md` is the identity of this Knowledge System of Record, and its
11
- prose will one day be the agent surface's system prompt. Do not draft it from
12
- guesses — interview the owner, one question at a time, and write down what
13
- they actually say.
11
+ prose IS the agent surface's system prompt (`ksor serve` wires it into the MCP
12
+ server's instructions). Do not draft it from guesses — interview the owner, one
13
+ question at a time, and write down what they actually say.
14
14
 
15
15
  ## The six questions
16
16
 
@@ -49,10 +49,15 @@ act on:
49
49
  record's **display title**, the human name every page will lead with
50
50
  ("Acme Operations Handbook", not the slug) — then the authority sentence,
51
51
  boundary, audience, and strictness — plain prose, written for a reader
52
- who must act on it. Leave the frontmatter keys alone, with one exception:
53
- an audience model from question 6 is written there as `audiences:` (a
54
- list) and `default_visibility:`, and `pnpm check` will hold the record to
55
- it from that moment on.
52
+ who must act on it. Leave the identity frontmatter keys alone; two things
53
+ are written there when they apply: an audience model from question 6, as
54
+ `audiences:` (a list) and `default_visibility:` (`pnpm check` holds the
55
+ record to it from that moment on); and — only when the owner stands up the
56
+ served MCP rung — the `database:`/`embedding:`/`retrieval:` blocks (see
57
+ `AGENTS.md` → "Serving to agents"; that is a later climb, not part of this
58
+ interview). The strictness answer from question 5 is the intent behind the
59
+ `retrieval.vector_floor` on that climb, measured by `ksor calibrate` — capture
60
+ it in the prose now so it is ready.
56
61
  - Restart `pnpm dev` afterwards so the site picks the new title up, and
57
62
  show the owner their name on the page.
58
63
  - Offer to capture the source list from question 4 as the first real
@@ -5,22 +5,25 @@ here; every coding agent reads this file first.
5
5
 
6
6
  ## The two worlds
7
7
 
8
- | Path | What it is |
9
- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
10
- | `knowledge/` | **the record** — governed markdown, the owner's world, the product |
11
- | `system/` | **the system** — all code that serves the record |
12
- | `instance.md` | what this SoR is authoritative for; its prose is the future agent surface's system prompt. Its `name:` is the machine identity (llms.txt, future citations) and its body `# H1` is the DISPLAY TITLE every page leads with — both read when the server or build STARTS, so restart `pnpm dev` after changing either (found live 2026-08-18) |
8
+ | Path | What it is |
9
+ | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
10
+ | `knowledge/` | **the record** — governed markdown, the owner's world, the product |
11
+ | `system/` | **the system** — all code that serves the record |
12
+ | `instance.md` | what this SoR is authoritative for; its prose IS the agent surface's system prompt (`ksor serve` wires the body into the MCP server's instructions). Its `name:` is the machine identity (llms.txt, citations) and its body `# H1` is the DISPLAY TITLE every page leads with — both read when the server or build STARTS, so restart `pnpm dev` after changing either (found live 2026-08-18) |
13
13
 
14
14
  The record survives the system: `knowledge/` must stay readable and complete
15
15
  even if `system/` is deleted. Dependency flows one way — the system reads the
16
16
  record; the record never references the system.
17
17
 
18
18
  `instance.md` carries a closed key set — `format`, `name`, `ksor`, `site`,
19
- and the optional pair `audiences` + `default_visibility` (the record's reader
19
+ the optional pair `audiences` + `default_visibility` (the record's reader
20
20
  audiences, ordered least- to most-restricted with `public` first, and the one
21
- a document takes when it names none — declared together or not at all) and
22
- everything that matters about it is the prose below that frontmatter;
23
- `pnpm check` names any other key rather than ignoring it.
21
+ a document takes when it names none — declared together or not at all), and
22
+ the four serve-config blocks `database` / `embedding` / `retrieval` / `budgets`
23
+ (present only once you climb to the served MCP rung — see "Serving to agents"
24
+ below; a `pnpm dev`-only project declares none). Everything else that matters
25
+ about the instance is the prose below the frontmatter; `pnpm check` names any
26
+ other key rather than ignoring it.
24
27
 
25
28
  ## Critical rules
26
29
 
@@ -37,12 +40,141 @@ everything that matters about it is the prose below that frontmatter;
37
40
  ## Commands (run at the repo root)
38
41
 
39
42
  ```sh
40
- pnpm install # once, after cloning or scaffolding
43
+ pnpm install # once, after cloning or scaffolding (also fetches the pinned `ksor` tool)
41
44
  pnpm dev # the site, hot-reloading, at http://localhost:3000
42
45
  pnpm build # static site into system/site/out/
43
46
  pnpm check # the format checker — run before handing off any knowledge change
44
47
  ```
45
48
 
49
+ ## Serving to agents — the MCP rung (needs Postgres + a provider key)
50
+
51
+ `ksor serve` runs an MCP server over the record so agents get cited retrieval
52
+ with honest abstention. It is the climbed rung — not required for `pnpm dev`.
53
+ Stand it up in this order (each step's errors explain how to fix themselves):
54
+
55
+ 1. **Configure `instance.md`.** One block is required — the name of the
56
+ environment variable holding your DSN (never the DSN itself):
57
+
58
+ ```yaml
59
+ database:
60
+ dsn_env: KSOR_DB_URL
61
+ ```
62
+
63
+ That is enough. `embedding:` is optional and already defaults to
64
+ `provider: gemini`, `model: gemini-embedding-001`, `dim: 1536`; write it out
65
+ only to pin the space explicitly or to change it — and note that model and
66
+ dim are the PERSISTED identity of the embedding space, so changing either
67
+ later means re-embedding the whole corpus. Keep `dim` at or below 2000: the
68
+ pgvector HNSW index refuses more, and `gemini-embedding-001` can emit 3072.
69
+
70
+ Leave `retrieval:` out for now — the gate is off and the server says so.
71
+ Turning it on is step 4, AFTER the record is serving.
72
+
73
+ 2. **Copy `.env.example` to `.env`** and fill it in — `ksor` reads it
74
+ automatically, so nothing needs exporting, and `.env` is already gitignored.
75
+ A real environment variable still wins over the file, so CI and production
76
+ overrides behave normally.
77
+
78
+ ```sh
79
+ cp .env.example .env
80
+ ```
81
+
82
+ Three values matter:
83
+
84
+ - `KSOR_DB_URL` — the Postgres store named by `instance.md`'s `dsn_env`. It
85
+ needs the pgvector extension: `CREATE EXTENSION vector;`
86
+ - `GEMINI_API_KEY` — the embedding provider key.
87
+ - `KSOR_AUTH_DISABLED=1` — **required for a local run.** `ksor serve`
88
+ refuses to boot unauthenticated without it, deliberately, so a server is
89
+ never left open by accident. It binds loopback, where auth off is the
90
+ intended dev shape. A PUBLIC deployment configures the SSO door instead —
91
+ see the comments in `.env.example` and "Serving safely" below.
92
+
93
+ 3. **Bring it up — one command:**
94
+
95
+ ```sh
96
+ pnpm up # schema → grant → ingest → serve
97
+ ```
98
+
99
+ Every step is re-runnable, so this is also how you **refresh after editing
100
+ `knowledge/`**: an applied schema reports "already applied", an existing
101
+ grant reports "already granted", and ingest builds a fresh generation.
102
+
103
+ **`pnpm up` ingests every time — but it re-EMBEDS nothing that has not
104
+ changed.** Chunks carry forward by content hash, so a rerun on an untouched
105
+ corpus makes zero provider calls (`embedded 0, carried N` in the output).
106
+ What it does spend is a generation: each run creates and activates a new one,
107
+ and they accumulate. So:
108
+
109
+ | You want to | Run |
110
+ | -------------------------------- | ------------------------------------------ |
111
+ | set up, or refresh after an edit | `pnpm up` |
112
+ | just restart the server | `pnpm serve` — no new generation |
113
+ | reap superseded generations | `pnpm exec ksor gc --instance instance.md` |
114
+
115
+ Run the steps individually (`pnpm schema`, `pnpm grant`, `pnpm ingest`)
116
+ when the acts belong to different people — a DBA holding the credentials
117
+ that authorize ingest, for instance.
118
+
119
+ 4. **Turn the abstention gate on — deliberately, once it serves.** This is the
120
+ step that makes "not in this corpus" a real answer, and it is measured, never
121
+ guessed:
122
+
123
+ ```sh
124
+ pnpm exec ksor calibrate --instance instance.md
125
+ ```
126
+
127
+ It prints a recommended `vector_floor` for THIS corpus in THIS embedding
128
+ space. Paste the number in and restart:
129
+
130
+ ```yaml
131
+ retrieval:
132
+ vector_floor: 0.55 # measured by ksor calibrate on <date>
133
+ ```
134
+
135
+ Never copy a floor from another corpus — recalibrate, and record the
136
+ measurement beside the number. Writing `vector_floor: uncalibrated` declares
137
+ the intent to gate WITHOUT a measurement, and every serve refuses until a
138
+ number replaces it; that is the fail-closed posture, not a starting point.
139
+
140
+ ```sh
141
+ pnpm schema # apply the DDL (once)
142
+ pnpm grant # authorize ingest for this corpus (once)
143
+ pnpm ingest # embed knowledge/ into a generation and activate it
144
+ pnpm serve # run the MCP server; any other verb: pnpm exec ksor <verb>
145
+ ```
146
+
147
+ ### How serving updates work (the generation model)
148
+
149
+ Each `ksor ingest` builds a **fresh generation** (invisible until activated) and
150
+ carries every unchanged embedding forward from the last complete generation —
151
+ so **re-ingest is safe and cheap**; only changed or failed chunks re-embed.
152
+ `--flip` swaps the active pointer, guarded by a catastrophic-shrink check
153
+ (`KSOR_MAX_SHRINK`, default `0.15` — a flip that drops more than 15% of nodes
154
+ refuses; override with `KSOR_ALLOW_SHRINK=1` when the shrink is intended). The
155
+ previous generation stays as a rollback target; `pnpm exec ksor gc` collects
156
+ abandoned ones.
157
+
158
+ ### Serving safely (fail-closed posture)
159
+
160
+ `pnpm serve` binds **loopback with auth off** — safe for local use. A **public**
161
+ bind refuses to boot unless auth is configured (`KSOR_SSO_URL` +
162
+ `KSOR_MCP_RESOURCE_URL` + `KSOR_JWT_ALLOWED_AUDIENCES`, making it an OAuth
163
+ Resource Server) OR you deliberately set `KSOR_ALLOW_PUBLIC_UNAUTHENTICATED=1`.
164
+ Never let a dropped auth variable silently ship an open door. On a non-loopback
165
+ bind, set `KSOR_ALLOWED_HOSTS` / `KSOR_ALLOWED_ORIGINS`; on more than one
166
+ replica, set a shared `KSOR_SNAPSHOT_KEYS` (unset ⇒ a per-process key, so a
167
+ search token minted by one replica fails on another).
168
+
169
+ Two things worth being deliberate about:
170
+
171
+ - **`KSOR_ALLOW_PUBLIC_UNAUTHENTICATED=1` serves your whole record to anyone
172
+ who can reach the port.** It exists for deployments fronted by your own
173
+ gateway or network policy. If nothing else is in front, do not set it.
174
+ - **Set `KSOR_SSO_ISSUER` when your SSO stamps a stable `iss`.** Audience is
175
+ always enforced against `KSOR_JWT_ALLOWED_AUDIENCES`; naming the issuer adds
176
+ one more check for the cost of one variable.
177
+
46
178
  ## Publishing
47
179
 
48
180
  `pnpm build` emits a fully static site (`system/site/out/`) deployable to
@@ -18,7 +18,39 @@ pnpm dev # browse the knowledge at http://localhost:3000
18
18
  ```
19
19
 
20
20
  No pnpm? Run `npm install -g pnpm` — or `corepack enable pnpm` on Node
21
- versions that bundle corepack.
21
+ versions that bundle corepack. The first `pnpm install` also fetches the
22
+ `ksor` tool (pinned in `package.json`) and writes it into your lockfile —
23
+ commit the updated lockfile.
24
+
25
+ ### Serving to agents
26
+
27
+ The record's other surface is an MCP server for AI agents — the same
28
+ knowledge, cited, with honest abstention. It is the climbed rung: it needs a
29
+ Postgres store (with pgvector) and an embedding provider key, so it is not
30
+ part of `pnpm dev`. The ordered path is:
31
+
32
+ ```sh
33
+ cp .env.example .env # fill in KSOR_DB_URL, GEMINI_API_KEY, KSOR_AUTH_DISABLED=1
34
+ pnpm up # schema → grant → ingest → serve
35
+ ```
36
+
37
+ `ksor` reads `.env` automatically — nothing to export. `KSOR_AUTH_DISABLED=1`
38
+ is required for a local run: serve refuses to boot unauthenticated on purpose,
39
+ so a server is never open by accident.
40
+
41
+ Add one block to `instance.md` first — `database: { dsn_env: KSOR_DB_URL }`,
42
+ the NAME of the variable, never the DSN. That is the whole required config:
43
+ `embedding:` already defaults to Gemini at 1536 dimensions, and leaving
44
+ `retrieval:` out starts you with the abstention gate off and honest about it
45
+ (turn it on afterwards with `ksor calibrate`, once the record is serving).
46
+
47
+ `pnpm up` is re-runnable — it is also how you refresh after editing
48
+ `knowledge/`. It re-ingests each time but re-embeds only what changed, so an
49
+ untouched corpus costs no provider calls; to simply restart the server without
50
+ building a new generation, run `pnpm serve` on its own. `AGENTS.md` → "Serving to agents" is the
51
+ full runbook; your coding agent reads it first. `pnpm serve` binds loopback
52
+ with auth off for local use; a public bind fails closed unless auth is
53
+ configured. Any other operation is `pnpm exec ksor <verb>`.
22
54
 
23
55
  Then talk to your coding agent — `AGENTS.md` carries the working rules, and
24
56
  the agent kit in `.agents/skills/` knows how to interview you
@@ -34,7 +66,7 @@ different coding agent's way of finding the same working contract.
34
66
  | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
35
67
  | `knowledge/` | **the record** — your governed markdown. The product; everything else serves it. |
36
68
  | `system/` | the code that serves the record: the site today, more as you need it. |
37
- | `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 renaming). When the agent surface ships, this prose becomes its system prompt. |
69
+ | `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 renaming). This prose IS the agent surface's system prompt `ksor serve` wires it into the MCP server's instructions. |
38
70
  | `AGENTS.md` | the working contract every coding agent reads first — the rules for writing knowledge here. |
39
71
  | `CLAUDE.md` | one line, pointing at `AGENTS.md`. Claude Code looks for this filename, not that one. |
40
72
  | `.agents/skills/` | the agent kit: `intake-interview` (define the record with you), `add-sources` (turn source material into governed documents), `format-checker` (the rules, as a program). |
@@ -42,8 +74,9 @@ different coding agent's way of finding the same working contract.
42
74
  | `.gemini/settings.json` | points Gemini CLI at `AGENTS.md`; Gemini does not read that filename on its own. |
43
75
  | `.github/workflows/validate.yml` | your CI: runs the same checker on every pull request and push to main. |
44
76
  | `.gitattributes` | markdown is checked out byte-stable on every platform, so the same commit hashes the same everywhere. |
45
- | `.gitignore` | keeps build output, `node_modules/`, and `.env*` out of the record's history. |
46
- | `package.json` | the `pnpm dev` / `pnpm build` / `pnpm check` commands, and the pnpm version this project pins. |
77
+ | `.env.example` | the variables the served rung needs; copy to `.env` (gitignored) and fill in. |
78
+ | `.gitignore` | keeps build output, `node_modules/`, and `.env` out of the record's history. |
79
+ | `package.json` | the `pnpm dev` / `pnpm build` / `pnpm check` commands and the served rung's `pnpm up` (schema → grant → ingest → serve, or run them separately), the pinned `@panaversity/ksor` tool, and the pnpm version this project pins. |
47
80
  | `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. |
48
81
  | `pnpm-lock.yaml` | the exact dependency versions — the reason two machines build the same site. |
49
82
 
@@ -0,0 +1,23 @@
1
+ # Copy to .env — ksor reads it automatically, and .env is gitignored.
2
+ # A real environment variable always wins over this file, so CI and production
3
+ # keep their own values.
4
+
5
+ # The Postgres store, named by instance.md's database.dsn_env.
6
+ # Needs the pgvector extension: CREATE EXTENSION vector;
7
+ KSOR_DB_URL=postgresql://user:password@host:5432/dbname
8
+
9
+ # The embedding provider key. instance.md defaults to gemini-embedding-001.
10
+ GEMINI_API_KEY=
11
+
12
+ # Local development posture. `ksor serve` REFUSES to boot unauthenticated
13
+ # without this — deliberately, so a server is never open by accident. It binds
14
+ # loopback, where auth off is the intended dev shape.
15
+ #
16
+ # For a PUBLIC deployment, delete this line and configure the SSO door instead:
17
+ # KSOR_SSO_URL=https://your-sso.example.com
18
+ # KSOR_MCP_RESOURCE_URL=https://your-host.example.com/mcp
19
+ # KSOR_JWT_ALLOWED_AUDIENCES=https://your-host.example.com/mcp
20
+ # Serving a public bind with auth off additionally requires
21
+ # KSOR_ALLOW_PUBLIC_UNAUTHENTICATED=1, which serves your whole record to anyone
22
+ # who can reach the port.
23
+ KSOR_AUTH_DISABLED=1
@@ -12,5 +12,6 @@ system/site/.staged-knowledge/
12
12
 
13
13
  # secrets never enter the record — system/ is their future home (serve)
14
14
  .env*
15
+ !.env.example
15
16
 
16
17
  .DS_Store
@@ -18,8 +18,9 @@ the single most important sentence in the project._
18
18
 
19
19
  Everything below this frontmatter is the identity of this instance: what the
20
20
  corpus covers, who it serves, and how strictly it should decline questions it
21
- does not cover. When the agent surface ships, this prose becomes its system
22
- prompt — write it for a reader who must act on it.
21
+ does not cover. This prose IS the agent surface's system prompt `ksor serve`
22
+ wires it into the MCP server's instructions so write it for a reader who must
23
+ act on it.
23
24
 
24
25
  Ask your coding agent to run the **intake interview** (it knows how — see
25
26
  `.agents/skills/intake-interview/`), answer its questions, and let it write
@@ -6,7 +6,15 @@
6
6
  "scripts": {
7
7
  "dev": "pnpm -C system/site dev",
8
8
  "build": "pnpm -C system/site build",
9
- "check": "node .agents/skills/format-checker/check.mjs"
9
+ "check": "node .agents/skills/format-checker/check.mjs",
10
+ "up": "pnpm schema && pnpm grant && pnpm ingest && pnpm serve",
11
+ "schema": "ksor schema --instance instance.md --apply",
12
+ "grant": "ksor grant --instance instance.md",
13
+ "ingest": "ksor ingest --instance instance.md --knowledge knowledge --flip",
14
+ "serve": "ksor serve"
15
+ },
16
+ "dependencies": {
17
+ "@panaversity/ksor": "KSOR-STAMP-VERSION"
10
18
  },
11
19
  "engines": {
12
20
  "node": ">=24"
@@ -8,12 +8,28 @@ packages:
8
8
  # `pnpm install` here never picks up a day-zero compromised release.
9
9
  minimumReleaseAge: 2880
10
10
 
11
+ # The one exception is the tool that scaffolded this project: `ksor init` pins
12
+ # `@panaversity/ksor` to the EXACT version the adopter deliberately ran, so the
13
+ # quarantine (a defense against unknown third-party releases) must not block the
14
+ # first install for 48 hours after every ksor release. Remove this line only if
15
+ # you unpin the dependency.
16
+ minimumReleaseAgeExclude:
17
+ - "@panaversity/ksor"
18
+
11
19
  # Dependency install scripts are denied by default. Flip an entry to true
12
- # only with a comment naming what breaks without it. esbuild and sharp are
13
- # reviewed and stay denied: both ship prebuilt platform binaries as
14
- # optionalDependencies, so their install scripts are download fallbacks the
15
- # site never needs — but pnpm 11 exits 1 on every install/dev until each is
16
- # explicitly decided. (found live: fresh-scaffold pnpm dev, 2026-08-18)
20
+ # only with a comment naming what breaks without it. pnpm 11 exits 1 on every
21
+ # install until each build script is explicitly decided (found live:
22
+ # fresh-scaffold pnpm dev, 2026-08-18). All four below are reviewed and stay
23
+ # denied:
24
+ # esbuild, sharp prebuilt platform binaries ship as optionalDependencies,
25
+ # so their install scripts are download fallbacks the site never needs.
26
+ # @google/genai, protobufjs — pulled in by the `@panaversity/ksor` serve
27
+ # tool. genai's flagged script is its own dev `prepare` (nothing the
28
+ # published tarball needs built); protobufjs's postinstall is a
29
+ # version-compat warning, nothing built. (Same review as the root
30
+ # workspace, 2026-08-19; serve works with both denied — verified live.)
17
31
  allowBuilds:
18
32
  esbuild: false
19
33
  sharp: false
34
+ "@google/genai": false
35
+ protobufjs: false
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://openapi.vercel.sh/vercel.json",
3
3
  "framework": null,
4
- "installCommand": "pnpm install --frozen-lockfile",
4
+ "installCommand": "pnpm install --no-frozen-lockfile",
5
5
  "buildCommand": "pnpm build",
6
6
  "outputDirectory": "system/site/out",
7
7
  "trailingSlash": true