Skip to main content
Glama

hello-world

A Knowledge System of Record: the governed source of knowledge this project's people and AI agents operate from.

Three things live here, and they are not the same kind of thing:

  • knowledge/ — the record. Plain governed markdown (plus the optional study attachments a document may carry). Yours forever, readable anywhere, portable without this repository's code.

  • system/ — the system. The site, and later the services, that serve the record. Replaceable machinery.

  • .ksor/governance.yaml — the authority. Which audiences exist, who may approve a document, who may take one down. Every governance act is checked against it, so the record never claims authority nobody granted.

The point of all this is the agent interface: an MCP door that answers from the record with citations, and declines — in as many words — when the record does not cover the question. That refusal is the feature; an agent that improvises over a gap is the thing a system of record exists to prevent. Beside it, the human interface is a site your readers browse. Same record, same governance, different machinery: the site is files, the door is a process, and they deploy separately.

Contents


Quick start

npm install
npm run dev        # browse the knowledge at http://localhost:3000

Commit the lockfile your first install writes.

No lockfile ships with this scaffold: npm keeps ONE lock for the whole workspace, and the @panaversity/ksor version pinned in package.json was stamped by the CLI that scaffolded this project — it could not be resolved into a lock before it existed. Your FIRST npm install writes package-lock.json; run it before you push, and COMMIT the result — that lock is why two machines build the same site.

One honest difference from the pnpm scaffold: pnpm quarantines newly published dependency versions for 48 hours (minimumReleaseAge), so a routine install never picks up a day-zero compromised release. npm has no equivalent — the .npmrc here carries the install-script denial half of that posture, and this sentence is the disclosure of the half it cannot.


Related MCP server: mcp-rag-bridge

Explore the human interface

Before changing anything, get a feel for how the record behaves. With npm run dev running:

  • Edit a starter document. Change its body, save, watch the page update.

  • Add a new document. It appears on the dev site, marked — and it would be on no built surface at all, because a new document is status: draft. No page, no sidebar row, no /llms.txt entry. That is the system working, not a broken build.

  • Ask your coding agent to do the same. AGENTS.md carries the working rules; read it before you change how documents are written here. The kit in .agents/skills/ already knows this project: intake-interview (define the record with you), add-sources (turn what you have — files, or what you know — into governed documents), and format-checker (the rules, as a program — also what npm run check runs).

Treat the starters as scratch paper. They ship approved by ksor-starter/0.0.60 — a tool, not a person. Edit the body and that approval stays stamped on text nobody reviewed, and npm run check will not catch it, because the frontmatter is still internally consistent. So explore in them freely, but start nothing you intend to keep in one of them. Replacing them properly is below.

To see what a build actually produces:

npm run build      # the static site, into system/site/out/
npm run preview    # serve exactly those bytes

There is no start script, and that is not an omission: the site is a static export, so nothing serves it at runtime. npm run preview is node:http and nothing else — no dependency, no network fetch — so it works offline and behind a firewall, like the build itself.

It binds loopback, so it is reachable from this machine only. To open the built site from a container published with -p, a cloud dev box, or a phone on the same wifi, name the address on the command line — preview is plain node and does not read .env:

KSOR_PREVIEW_HOST=0.0.0.0 npm run preview

Serving to agents

This is the point of the whole thing. The record's other surface is an MCP server: the same knowledge, cited, with a measured floor under which it declines. An agent connected to it answers from your governed documents or says the record does not cover the question — and never quietly fills the gap from its training.

It asks for two things npm run dev does not: a Postgres store with pgvector, and an embedding provider key. Three steps, and the order is load-bearing — the command block is last because it needs both of the things above it.

1. Nothing to configure

instance.md already names the VARIABLE holding your DSN — never the DSN itself:

database:
  dsn_env: KSOR_DB_URL

That is the whole required config. embedding: defaults to Gemini at 1536 dimensions, and retrieval: is written for you by step 3's calibrate. Change the variable name here only if you want a different one.

2. Get a database — your agent can do this one

.mcp.json at the repo root declares the MCP servers this project may reach. It ships with two, and both are yours to keep or delete — it is your file:

server

what it is

Neon

Neon's own hosted MCP server, for provisioning the Postgres the agent surface needs (step 2)

agentfactory-system-of-record

a KSoR record Panaversity operates, served over MCP — an example of the surface you are building

The second is not your record and is not needed to run this project. It is read-only and it answers about the Agent Factory curriculum, not about your knowledge. Delete the entry if you would rather your agent not have it; nothing here depends on it.

Before you connect Neon, know what you are granting. The Neon MCP server acts on your Neon account, not on one database: an agent holding it can create and delete projects and branches. Point it at an account you are willing to let an agent change, review the plan it shows you before approving, and read Neon's own documentation on the server's scopes and permissions rather than taking this paragraph as the whole of it.

With it connected, ask your coding agent:

Using the Neon MCP server, create a project called <your-record> and enable the pgvector extension on it. Then create a branch called dev, and save that branch's connection string to .env as KSOR_DB_URL. Never print my API key. Show me the plan before you run anything.

Prefer the OAuth flow. .mcp.json is committed and carries no secret; an API key pasted into it would be.

Any Postgres with pgvector works — Neon is the path that has an MCP server, not a requirement. Locally: docker run -e POSTGRES_PASSWORD=x -p 5432:5432 pgvector/pgvector:pg17.

GEMINI_API_KEY is the one step no agent can do for you — no vendor mints an API key over a protocol. Get it from aistudio.google.com/apikey and paste it. The free tier costs nothing and is enough: embedding input on gemini-embedding-001 is free of charge, and a first corpus is a few thousand tokens. This is a signup, not a bill.

3. Fill in the environment

cp .env.example .env

Then set KSOR_DB_URL, GEMINI_API_KEY and KSOR_AUTH=disabled-local.

Point KSOR_DB_URL at a hosted Postgres now if you intend to deploy — the same one your host will use. Neon is what this project's own docs are measured against; pgvector is on every plan including the free one, and you enable it once per database with CREATE EXTENSION IF NOT EXISTS vector;. Anything with pgvector works. Quick deployment explains why one shared database saves you a step.

ksor reads .env automatically — there is nothing to export, and where a refusal tells you to export a variable, putting it in .env is the same thing. KSOR_AUTH=disabled-local is required for a local run: serve refuses to boot unauthenticated on purpose, so a server is never open by accident.

4. Bring it up

npm run provision  # once: apply the schema, authorize ingest
npm run refresh    # build, ingest the record, collect retired generations
npx ksor calibrate --instance instance.md   # measure the floor; paste the printed block into instance.md
npm run serve      # the MCP server, with the gate already on

npm run provision runs once — it applies the schema (or migrates it forward) and authorizes ingest, the two privileged acts that should not happen on every boot. After that, npm run refresh publishes what you have edited and npm run serve runs the server. They are separate because publishing is an act, not a side effect of starting a process.

calibrate is what makes "not in this corpus" a real answer, and it belongs here rather than later: it needs an ingested corpus but no running server, and the floor is read when the door boots — so measuring before npm run serve means the door comes up gated the first time. It prints a retrieval: block for THIS corpus in THIS embedding space; paste it into instance.md exactly as printed. Never copy a floor from another corpus.

It also prints its own caveat, and it is worth reading: the probes it writes are derived from your passages, so they share vocabulary a real question will not. The floor it reports is an upper bound on separation until you check it against questions a person wrote: in-corpus ones with --queries-file, and — because the built-in out-of-corpus probes are all far-domain — questions just outside your scope with --ooc-file.

A rerun on an unchanged record costs nothing: no new generation, no embedding, no rows. Edit a document and the next run picks up exactly that change.

AGENTS.md → "Serving to agents" is the full runbook; your coding agent reads it first. A public bind needs a configured SSO door rather than disabled-local — see The agent surface deploys separately. Any other operation is npx ksor <verb>.

Test the door with an actual agent

The MCP door is meant to be read by agents, so check it with one rather than with curl. With npm run serve running, add an entry to the .mcp.json you already have — alongside Neon, not in place of it:

    "test-record": {
      "type": "http",
      "url": "http://127.0.0.1:8080/mcp"
    }

If you skipped calibrate, expect answers where this test wants refusals — the gate is off until a floor is measured, which the server says at boot (abstain OFF) and in every search envelope (gate: "off"). That is honest, not broken: a floor nobody measured would be a number pretending to be a guarantee.

Open a new session of your coding agent, confirm it lists the server, then ask it three questions in this order — the order is the test:

  1. Something the record covers, phrased in words the document never uses. Retrieval is semantic, so this should still find it, and every answer should arrive with a citation.

  2. Something adjacent but not covered — your record's own subject area, a question it genuinely does not answer. It should decline.

  3. Something far outside the record. It should decline, and must not answer from its own knowledge.

Question 2 is the one that matters. Anything can answer questions it has the text for; refusing a plausible near-miss is the property that makes a system of record worth trusting, and it is the one that breaks quietly.

Keep the test-record entry or remove it — it points at loopback and holds no secret either way. Do not delete .mcp.json itself: it is where Neon is declared, and step 2 above needs it.


Quick deployment

Both surfaces on one domain, in about ten minutes:

  1. Push the repository to GitHub.

  2. Import it in Vercel, then set Framework Preset to Services and Root Directory to ./. The preset is not cosmetic: Vercel's own guide says a project builds as services only when the preset is Services AND vercel.json carries a services key, and that "if either is missing, Vercel falls back to its default framework detection and ignores your services configuration" — which is the silent 404 below. No file in this repository can set it for you.

    As for Root Directory: Vercel auto-fills it with system/site, because that is where it finds a framework — and the build then reads system/site/vercel.json, which does not exist, and fails with Project framework is set to "services", but no services are declared. The services ARE declared, in vercel.json at the repo root, which is the only place they can be: one builds the site, the other builds a container from the root Dockerfile.

  3. Set three environment variables in Vercel: KSOR_DB_URL, GEMINI_API_KEY, and KSOR_AUTH=disabled-public.

  4. Check it actually serves, before you tell anyone the URL. A Ready deployment that answers 404 everywhere looks identical to a good one from the dashboard:

    B=https://your-record.vercel.app
    curl -o /dev/null -w '%{http_code}\n' "$B/"           # expect 200
    curl -o /dev/null -w '%{http_code}\n' "$B/llms.txt"   # expect 200
    curl -sI "$B/mcp" | head -1                           # expect 405

    /mcp answering 405 is the door refusing a GET — that is how you know it is routed at all, and a 404 there means the services block was ignored.

Three things catch people here. Two are the system being deliberate; the first is not, and it is the one that fails without saying so:

  • A deployment can report Ready and serve nothing. The build succeeds, Vercel collects nothing, and the deployment takes your domain and answers 404: NOT_FOUND everywhere — with one build-log line as the only signal: WARNING! Build output contains no "functions" or "static" directory. That warning is the FALLBACK collector finding nothing, which is what step 2's preset exists to prevent — check it first. One measurement of ours disagrees with the vendor's rule and is recorded in node_modules/@panaversity/ksor/docs/deploying.md; the honest state is that the preset is necessary by the vendor's documentation and has once appeared not to be. If you hit this, the fallback is the classic-keys form in node_modules/@panaversity/ksor/docs/deploying.md — read it there rather than guessing, because it moves the door off your domain and KSOR_MCP_RESOURCE_URL and your SSO API Identifier both have to move with it.

  • disabled-local will not deploy. The container sets $PORT, so the door binds 0.0.0.0 — a PUBLIC bind — and refuses that value by design, saying so in as many words. disabled-public is you saying you know the door is reachable from outside itself. It belongs in Vercel's environment, not in your .env, so your local npm run serve keeps its loopback posture. It is a starting posture, not a destination: secure it properly with the SSO variables once the thing is up.

  • Point KSOR_DB_URL at a Postgres your host can reach. Deploying does not publish — the door serves whatever generation is already in the database. If your DSN pointed at a database on your laptop, the site comes up fine and the door comes up empty. Use one hosted Postgres for both and the npm run refresh you already ran is the generation Vercel serves.

If you only want readers served, the site is a static export with no database in the loop: deploy with no environment variables at all and add the door later.


Make the record yours: replace the starters

Five starter documents publish on the first build. They ship status: stable, so npm run dev and npm run build both give you a working record straight away — pages, a sidebar, a /llms.txt an agent can read — instead of an empty shelf. They are approved by ksor-starter/0.0.60: the tool that wrote them, named as a producer rather than as a person, because no person reviewed a word of it. That is what the trust tier unverified on every one of those pages says, and it is true.

They also describe KSoR, not your organisation — and a record that describes the wrong thing describes it on every surface. So replace them, in this order:

  1. Run the intake interview. Ask your coding agent for it. Three questions — what this record is the final word on, what sits just outside it, and who may approve or withdraw a document — then it writes instance.md with you and replaces the human:you placeholder in .ksor/governance.yaml with your real handle.

  2. Write and approve at least one document of your own — ask your agent to add it (the add-sources skill), read it on npm run dev, then approve it. A record is never empty: delete all five starters before this and the next build refuses ksor-record-empty and writes nothing.

  3. Delete each starter document as your own knowledge arrives.

  4. When the last one is gone, delete ksor-starter/0.0.60 from approval_authorities in .ksor/governance.yaml. Nothing of yours should be approved by a tool.

If you did the hello-world tutorial first, your document is approved by human:you. The interview replaces that placeholder with your real handle — and re-attributes every act already recorded under it to you in the same change, because it is the same person. A policy that no longer names human:you beside a document that still cites it refuses ksor-approver-unauthorised.


Writing knowledge

A new document publishes nothing

A new document is status: draft, and npm run build admits a draft to no surface at all: no page, no sidebar row, no /llms.txt entry, nothing for an agent to read. npm run dev shows it, marked — the preview is where drafts live.

Publishing adds two keys

Beside status: stable, name what produced the text and who approved it. Both, or npm run check refuses the document:

status: stable
generated: { by: "human:you", at: 2026-01-31T09:00:00Z }
ksor:
  audience: [public] # already there — every document carries it, drafts too
  approval: { by: "human:you", at: 2026-01-31T09:00:00Z }

generated is provenance: it names whatever produced the text — a person, or the agent that drafted it — and nothing has to authorise it.

approval.by is authority, so it must name an actor .ksor/governance.yaml lists, and its at may not be earlier than generated.at — the text that was approved has to be the text that was written. That act is yours.

Run npm run check before you commit. It runs the rules as a program, and every failure it reports says what is wrong, why the rule exists, and how to fix it.

Names instead of handles

The record stores actors as identifiers — human:you, team:legal-ops — and a page would otherwise lead with the slug: "Owner · human:bashiraziz". Put the name beside the identifier in .ksor/people.yaml and pages print that instead:

people:
  "human:bashiraziz": Bashir Aziz
  "human:ciso": Ayesha Khan

Keyed by the identifier exactly as the record stores it, quoted because it contains a colon. There is no rule that turns a name into a handle — ciso is nobody's squashed full name — so both are written down, and an actor with no entry renders exactly as stored rather than being guessed at.

This is presentation, and nothing else. It grants no authority: who may approve or withdraw is .ksor/governance.yaml, and the two files are not checked against each other, because someone who leaves the authority list is still the recorded approver of everything they approved. Optional — a record that declares no names reads exactly as it did before.

Presenting a document

Ask your coding agent for slides and it writes them, from the document, into the record:

make slides for knowledge/expenses/approvals.md

Your agent reads the document whole, writes the deck into knowledge/expenses/approvals.slides.yaml, checks every claim and every number back against the document, and tells you what it left out because the document did not support it — which is usually how you find out a document has a gap. The deck then renders on that document's page, straight after its introduction: click through it inline, or Present for fullscreen. Presenter notes stay off the screen.

The slides live in the record, so they are reviewed in the same pull request as the document, versioned with it, and withdrawn when it is withdrawn. There is no third party and no link to rot. If you already keep a deck in Google Slides, Canva or SlideShare you can point at it instead — slides.url: rather than deck: — and the page will offer it as a link with a frame the reader loads on click, so nothing is requested from the host until somebody asks.

Summarising a document

Long documents get a Summary tab beside their own words, written the same way:

summarise knowledge/expenses/approvals.md

Your agent reads the document whole, writes knowledge/expenses/approvals.summary.md, and checks every line back against the document — every number, every rule, and every ## section, because a summary that covers the opening and trails off is worse than none: a reader who used it believes they have the whole document. It reports what it left out because the document did not support it.

The summary is part of its document, not a document of its own: no route, no sidebar row, no line in llms.txt, and it takes its governance from its parent. Ask for one only where there is something to compress — under about two screens, a summary that restates the page teaches readers the tab is not worth opening, and a good agent will say so rather than write one.


Deploying

Quick deployment covers the common path. This is the rest of it.

The site

npm run build writes HTML, JS and CSS to system/site/out/ — about 2 MB with zero host-specific dependencies. Anything that can serve files can serve it, and npm run preview serves exactly those bytes locally.

npm run build runs ksor build first. It generates every index.md, runs the record checker, and writes build.lock.json — the committed record of what was published, from which commit, with which toolchain — and only then builds the site. A checker refusal stops the build before anything is written.

  • Vercel — the shipped vercel.json deploys from the repo root, builds with npm run build, and serves system/site/out/. It also declares the MCP door as a second service built from the shipped Dockerfile, so /mcp and the site share one domain.

  • GitHub Pages, nginx, S3, anything static — run npm run build and upload system/site/out/. Hosted under a sub-path (like user.github.io/repo)? Build with KSOR_BASE_PATH=/repo npm run build.

Verify any deploy the same way: the home page, one document page and /llms.txt load, and each names the documents this record has approved. On a record whose documents are all still drafts, the home page and /llms.txt come up empty and there is no document page at all — which is the correct answer, not a broken deploy. Approve a document and rebuild to see it change.

Takedowns reach the site through a committed file

Takedowns reach the site through .ksor/takedowns.yaml, the committed ledger — a file in this repository, so the site build needs no database access at all.

That is deliberate. The act that withdraws a document is one merged commit, and both surfaces read it: the door refuses immediately, the site at its next build. Merge the ledger entry, rebuild, redeploy.

A withdrawal that arrives on a clock works the same way, and that one has to be scheduled. stale_after and ksor.effective_from are evaluated once per build, at the instant that build ran, and the answer is written into system/site/out/ — static files cannot re-decide themselves. So a document whose stale_after passes after your last build keeps appearing in /llms.txt and in its markdown twin, while ksor serve — a process, evaluating per request — already refuses it. ksor build prints the next instant at which this happens. Nothing here rebuilds for you: validate.yml runs on pull requests and vercel.json declares no cron. If this record uses either key, add a scheduled rebuild and redeploy.

The agent surface deploys separately

Dockerfile and .dockerignore at the repo root build it, and they name no host — the same image runs on Cloud Run, Fly, Render, ECS, Kubernetes or a VPS:

docker build -t my-record .
docker run --rm -p 8080:80 --env-file .env \
  -e KSOR_AUTH=disabled-public my-record

That last flag is not boilerplate, and it is not a workaround. The image sets $PORT, so the door binds 0.0.0.0 — a PUBLIC bind — and the KSOR_AUTH=disabled-local your .env carries refuses there by design. Your laptop is not the exception: a container really is reachable from outside itself, and disabled-public is you saying you know that. It goes on the command rather than into .env so your ordinary npm run serve keeps the loopback posture — and a real deployment sets it (or, better, the SSO variables) in the host's environment, since .dockerignore keeps .env out of the image entirely.

One thing surprises people: deploying does not publish. The door serves whatever generation is already in the database, so a first deploy with no npm run refresh serves an empty record. Publishing is a step you run — from your machine or from CI — and it is deliberately not something a booting container does. The full walkthrough, including what a cold start costs and where ingest belongs, is in node_modules/@panaversity/ksor/docs/deploying.md.

Audiences decide what a build contains

If .ksor/governance.yaml registers audiences, what you deploy is a viewer. Plain npm run build builds for [public] — safe for any host. KSOR_AUDIENCE=public,<audience> npm run build — a comma list that must always include public — builds for a wider viewer, and that build carries an "— not for publication" label because it must never reach a public host: put it behind access control you already trust (VPN, SSO proxy, authenticated host). The tiers govern what a build contains; where each build may be served is yours to enforce.

The site can also show a sign-in control that names the reader in the navbar. It is off until you set three variables (see .env.example), and it names people rather than keeping them out — a static export cannot gate itself, so it is worth having on a record already behind one of the answers above, and is not a substitute for them. Setup and the honest limits: node_modules/@panaversity/ksor/docs/deploying.md.


Reference

Commands

Command

What it does

When

npm install

fetches dependencies and the pinned ksor tool

first — then commit the lockfile

npm run dev

the site at :3000, drafts visible and marked

while you write

npm run check

runs the record checker as a program

before every commit

npm run build

ksor build (index pages, checker, build.lock.json), then the static site to system/site/out/

before a deploy

npm run preview

serves system/site/out/ — there is no start, the site is a static export

to check a build

npm run provision

applies the schema, authorizes ingest

once, for the agent surface

npm run refresh

builds, ingests the record, collects retired generations

every time you publish

npm run serve

the MCP server on :8080

to run the door locally

npx ksor <verb>

everything else — calibrate, takedown, …

as needed

The files, explained

Nothing here is decoration, and the dotfiles are not ceremony — each one is a different coding agent's way of finding the same working contract.

The record and its authority

Entry

What it is

knowledge/

the record — your governed markdown. The product; everything else serves it.

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 npm run dev after changing either). Its BODY is the agent surface's system prompt — ksor serve wires it into the MCP server's instructions.

.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.

.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.

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.

The system that serves it

Entry

What it is

system/

the code that serves the record: the site today, more as you need it.

package.json

the surface commands — npm run dev (the site) and npm run provision / npm run refresh / npm run serve (the agent surface) — plus npm run build / npm run preview / npm run check, the pinned @panaversity/ksor tool and the workspace layout.

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.

vercel.json

one domain, two services — the static site and the MCP door. Delete it if you deploy elsewhere.

.env.example

the variables the served rung needs; copy to .env (gitignored) and fill in.

Entry

What it is

.npmrc

dependency install scripts are denied; the comment inside discloses the one protection this scaffold lacks (a 48-hour quarantine on new releases).

package-lock.json

the exact dependency versions — written by your FIRST install; commit it, it is the reason two machines build the same site.

The working contract

Entry

What it is

AGENTS.md

the working contract every coding agent reads first — the rules for writing knowledge here.

CLAUDE.md

one line, pointing at AGENTS.md. Claude Code looks for this filename, not that one.

.agents/skills/

the agent kit: intake-interview (define the record with you), add-sources (turn files, or what you know, into governed documents), format-checker (the rules, as a program).

.claude/skills/

byte-identical copies of the kit — Claude Code discovers skills only here. The checker enforces the mirror, so the two cannot drift.

.gemini/settings.json

points Gemini CLI at AGENTS.md; Gemini does not read that filename on its own.

.github/workflows/validate.yml

your CI: runs the same checker on every pull request and push to main.

.gitattributes

markdown is checked out byte-stable on every platform, so the same commit hashes the same everywhere.

.gitignore

keeps build output, node_modules/, and .env out of the record's history — and negates three paths inside .ksor/, because the policy, the ledger and the phone book ARE the record.

format-checker deliberately contains a program, check.mjs, and not only prose: rules that are only written down cannot refuse anything.

Everything here is yours to change. The kit exists so that any coding agent can operate this project without being taught it first.

When something refuses you

This project refuses loudly and on purpose. Most of what looks like a failure is a rule doing its job — and every refusal names its own fix, so this table is a map rather than a substitute.

What you see

What it means

What to do

npm run check refuses a document

status: stable without both generated and ksor.approval, or an approval earlier than the text it approves

add both keys; approval cannot precede what it approves

start — missing script

there is none: the site is a static export, so nothing serves it at runtime

npm run preview, or upload the folder

npm run preview exits 3

no system/site/out/ yet, PORT is not a port number, the port is taken, or KSOR_PREVIEW_HOST cannot be bound — it says which

build first; or set a free PORT (dev uses 3000 too). preview binds loopback; set KSOR_PREVIEW_HOST to reach it from a container or another device

npm run serve refuses to boot

it will not run unauthenticated by accident

KSOR_AUTH=disabled-local in .env for a loopback run

the deployed or containerised door refuses with disabled-local

it binds 0.0.0.0 — a public bind

KSOR_AUTH=disabled-public in the host environment, or configure the SSO variables

the agent answers questions 2 and 3 instead of declining

no floor is measured, so the gate is off (abstain OFF, gate: "off") — step 3's calibrate was skipped

npx ksor calibrate --instance instance.md, paste the block, restart

a deployed door serves an empty record

deploying does not publish — and a laptop DSN is unreachable from the host

point both at one hosted Postgres, then npm run refresh

the home page and /llms.txt are empty

every document is still a draft — correct, not broken

approve one and rebuild

/llms.txt on the DEV server does not change after you approve

output: "export" requires its route handler to be static, so npm run dev computes it once per process — the document page beside it updates, that file does not

restart npm run dev; the built site is always current, so this is a dev-server artefact only

ksor-record-empty

every document was deleted — a record is never empty, so nothing was written

add one document of your own (or restore one from git) before deleting the last starter

ksor-approver-unauthorised

a document is approved by an actor .ksor/governance.yaml no longer names — usually human:you after the interview

re-attribute the approval to your handle, or restore the actor to approval_authorities

ksor-generated-stale at npm run build or npm run refresh

a stable document's body changed since a commit where it was stable, and generated.at was not advanced past that commit's (left alone, or moved backward) — the stamp dates the text, and npm run check reads no document history, so it passed

set generated.at to an instant after the edit and re-approve (ksor.approval.at may not precede it)

a new document never appears on the built site

drafts reach no built surface at all

publish it — status: stable plus both governance keys

an expired document still shows on the site but not through the door

the static build evaluated stale_after at build time

rebuild and redeploy; schedule a rebuild if you use it

Vercel: no services are declared

Root Directory was auto-filled with system/site

set it to ./

Dependencies and advisories

An audit of this scaffold reports vulnerabilities in next, and will keep doing so: a framework that large always has open advisories against whatever version you have pinned.

npm install prints the count at the end of every install, so you meet it before you have run anything, next to an invitation to run npm audit fix --force.

Never let an audit tool raise the pin for you. It moves off the version this scaffold was built and tested against, and that pin is the whole reason two machines produce the same site. Bump it deliberately instead — take the newer pin a newer ksor init emits, or raise it yourself and re-run npm run build.

It also reads worse than it is, for one structural reason worth knowing: this site is a static export. npm run build writes HTML, JS and CSS to system/site/out/, and no framework server ever runs in front of your readers — no middleware, no server actions, no rewrites, no image optimizer. Most framework advisories describe exactly those request paths, so they have nothing here to reach.

Two things that argument does NOT cover, and you should treat as real: an advisory in the build toolchain, which does run, on your machine and in your CI; and any advisory at all if you later add a served route and stop exporting. Read what an advisory affects before deciding it is inert — the static export is a reason, not a blanket.


Ownership

Everything here is yours. The scaffold was generated by ksor (version in instance.md) and is granted without attribution or licence obligations; your knowledge was never anyone else's to license.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to query and manage a document knowledge base via MCP, with RAG-powered search and grounded answers with citations.
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    An MCP server that acts as a governed customer-support tool, resolving questions only when the knowledge base supports a cited, grounded answer and honestly escalating everything else with provenance and evidence.
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables MCP clients to ask plain-language questions and receive answers grounded only in documents the configured role is cleared to read, with the same access-controlled tools available across any client.
    -