openvibers/openvibe.wiki Commits
AGENTS.md: the context an agent starts from (#301)
Changes
nnew file mode 100644@@@ -0,0 +1,100 @@++# AGENTS.md — OpenVibe.Wiki++++## Project overview++++OpenVibe.Wiki (`https://openvibe.wiki`) is the network's knowledge product: wiki spaces (official++editorial and community), page trees with canonical slugs, immutable revisions with diff and revert,++typed infoboxes, `[[internal links]]`, citations bound to the revision that used them, Media++attachments and Community discussion — server-rendered and useful without JavaScript.++++- **Entry:** `server/index.js`; app `server/app.js`; port `4800`, service id `wiki`.++- **API/pages:** `server/http/api.js` (`/api/v1`), `pages.js`, `machine.js`, `openapi.js`,++ `actor-limits.js`, `common.js`, `frame-groups.js`; rendering `server/render/` (`layout.js`,++ `views.js`, `blocks.js`, `short.js`); public CSS/JS in `public/`.++- **Wiki domain:** `server/wiki/` — `service.js`, `content.js`, `writer.js`, `access.js`,++ `images.js`, `review-kind.js`, `import.js`, `seed.js`, `vibe.js`, `account-data.js`.++- **Integrations:** `server/integrations/` — `platform.js` (Network/SSO), `image-fetch.js`,++ `projects-module.js`, `vip.js` (gated spaces/pages).++- **Docs:** `docs/threat-review.md`; seeds in `seeds/` and `scripts/seed.js`; `STATUS.json`.++++## Owns, does not own++++Owns the ten authority tables in `ov_wiki`: `wiki_spaces` (official/user, public/members/private),++`wiki_pages` (canonical identity, slug, tree via `parent_id`, publication state, published revision),++`wiki_page_revisions` (**immutable**; PL/pgSQL triggers refuse UPDATE/DELETE), plus the page++content/blocks, citations, discussion refs, media refs, review queue, shares, proposals and outbox.++++Does not own: discussion (Community owns threads; Wiki stores only `wiki_discussion_refs` and++renders/posts through Community); attachments (Media owns bytes; Wiki stores `med_…` ids and their++last known state); source items (Sources; a citation stores the item id plus URL, title, retrieval++time and licence read from its provenance); the network index (Search; Wiki sends++`wiki.index_document.*`). It also does not own identity (Network), entitlements (VIP/Billing) or the++projects module (Services).++++## Who calls it, what it calls++++People use their Network JWT (Bearer, or the `ov_token` cookie; cross-site cookie writes are refused);++services use client-credentials tokens for audience `openvibe.wiki` with one capability per route, and++content writes need the person in `X-OV-Subject` (whose space role applies). SDK `openvibe-sdk/wiki`++is the read client; `openvibe-sdk/community` is used for discussion. Wiki calls Network (SSO, JWKS,++names), Community (threads), Media (upload/read), Sources (citation provenance), Search (index events++out), Events (outbox), AI (`wiki` proposals/drafts) and VIP (gated spaces/pages). AI proposals are++drafts: a person reviews and publishes them.++++## Database and migrations++++PostgreSQL `ov_wiki` through `openvibe-sdk/db` (PgBouncer for the app, direct owner role for++migrations); `migrations/0001..0009` cover spaces/pages/revisions, review kinds, shares, the outbox++rejection index and defaults. Applied at boot; never edit one that has run. Revision immutability is++enforced by database triggers, not only by code. Tests use PGlite or `WIKI_TEST_STORE=pg`; a migration++that a rollback cannot tolerate is not allowed.++++## Auth and privacy++++Network RS256 JWTs (offline JWKS) and SSO. A person's space role decides writes; services act for a++person only with `X-OV-Subject` and the capability. Visitors without SSO read public content only; a++members/private space is gated through VIP and the entitlement check. Cross-site cookie writes are++refused. AI generates proposals/drafts that need a person's approval before publication; every seed++page is currently `noindex` because no person has reviewed it, and none is in Search. Public pages are++cacheable; a signed-in page is `private, no-store`.++++## Deploy++++`sudo ovhost deploy wiki` (git-checkout: fast-forward `/opt/openvibe.wiki`, install on a lockfile++change, restart, wait for `/api/ready`; rollback `sudo ovhost rollback wiki --to <sha>`). Unit++`openvibe-wiki.service` on `127.0.0.1:4800`, env `/etc/openvibe/wiki.env`, database `ov_wiki`, nginx++`deploy/nginx/openvibe.wiki.conf`; the release migrates at boot.++++## Commands and tests++++```bash++npm ci++npm run dev # http://127.0.0.1:4800++npm test # every test/*.test.js, temp PGlite + stub upstreams++npm test -- revisions seo # only matching files++npm run test:pg # the same on PostgreSQL++npm run seed # the official space's seed pages++```++++`test/run.js` uses `openvibe-shared/test-runner` (60 s, serial): temporary PGlite, a generated RSA key++and stub upstreams, never the network. Coverage includes page trees/slugs, immutable revisions and++diff/revert, blocks/infoboxes/internal links, citations and the citation inspector, Media and++Community integration, access/VIP/delegation, AI proposals/review, discovery/SEO, account data and the++empty states.++++## Conventions and pitfalls++++- CommonJS, 4-space indent, single quotes, semicolons; RFC 9457 problem+json, escaped HTML and a safe++ block renderer (user content never becomes raw markup).++- **A revision is immutable.** Publish a new revision; never UPDATE/DELETE a `wiki_page_revisions` row++ or bypass the trigger. Diff/revert and citations are computed from revisions.++- **Citations are provenance, not decoration:** store the Sources item id, URL, title, retrieval time++ and licence; a changed/removed source must be visible as such, never silently rewritten.++- **Discussion and bytes live elsewhere:** store the Community thread id / Media `med_` id, verify the++ access with the owning service, and never copy a thread or a file into Wiki.++- **Seed/AI text is unreviewed until a person approves it:** keep `noindex` and out of Search until++ reviewed; AI drafts are proposals, not publications.++- **Slug and tree changes are redirects:** a moved page keeps its canonical identity and leaves a++ redirect; `[[internal links]]` resolve through the canonical slug.++- **Search is downstream:** publish/update/remove through `wiki.index_document.*` in the transaction++ (or the outbox), never write Search's tables.nnew file mode 100644@@@ -0,0 +1 @@
new file mode 100644
new file mode 100644
@@ -0,0 +1,100 @@
@@ -0,0 +1,100 @@
+# AGENTS.md — OpenVibe.Wiki
+
+## Project overview
+
+OpenVibe.Wiki (`https://openvibe.wiki`) is the network's knowledge product: wiki spaces (official
+editorial and community), page trees with canonical slugs, immutable revisions with diff and revert,
+typed infoboxes, `[[internal links]]`, citations bound to the revision that used them, Media
+attachments and Community discussion — server-rendered and useful without JavaScript.
+
+- **Entry:** `server/index.js`; app `server/app.js`; port `4800`, service id `wiki`.
+- **API/pages:** `server/http/api.js` (`/api/v1`), `pages.js`, `machine.js`, `openapi.js`,
+ `actor-limits.js`, `common.js`, `frame-groups.js`; rendering `server/render/` (`layout.js`,
+ `views.js`, `blocks.js`, `short.js`); public CSS/JS in `public/`.
+- **Wiki domain:** `server/wiki/` — `service.js`, `content.js`, `writer.js`, `access.js`,
+ `images.js`, `review-kind.js`, `import.js`, `seed.js`, `vibe.js`, `account-data.js`.
+- **Integrations:** `server/integrations/` — `platform.js` (Network/SSO), `image-fetch.js`,
+ `projects-module.js`, `vip.js` (gated spaces/pages).
+- **Docs:** `docs/threat-review.md`; seeds in `seeds/` and `scripts/seed.js`; `STATUS.json`.
+
+## Owns, does not own
+
+Owns the ten authority tables in `ov_wiki`: `wiki_spaces` (official/user, public/members/private),
+`wiki_pages` (canonical identity, slug, tree via `parent_id`, publication state, published revision),
+`wiki_page_revisions` (**immutable**; PL/pgSQL triggers refuse UPDATE/DELETE), plus the page
+content/blocks, citations, discussion refs, media refs, review queue, shares, proposals and outbox.
+
+Does not own: discussion (Community owns threads; Wiki stores only `wiki_discussion_refs` and
+renders/posts through Community); attachments (Media owns bytes; Wiki stores `med_…` ids and their
+last known state); source items (Sources; a citation stores the item id plus URL, title, retrieval
+time and licence read from its provenance); the network index (Search; Wiki sends
+`wiki.index_document.*`). It also does not own identity (Network), entitlements (VIP/Billing) or the
+projects module (Services).
+
+## Who calls it, what it calls
+
+People use their Network JWT (Bearer, or the `ov_token` cookie; cross-site cookie writes are refused);
+services use client-credentials tokens for audience `openvibe.wiki` with one capability per route, and
+content writes need the person in `X-OV-Subject` (whose space role applies). SDK `openvibe-sdk/wiki`
+is the read client; `openvibe-sdk/community` is used for discussion. Wiki calls Network (SSO, JWKS,
+names), Community (threads), Media (upload/read), Sources (citation provenance), Search (index events
+out), Events (outbox), AI (`wiki` proposals/drafts) and VIP (gated spaces/pages). AI proposals are
+drafts: a person reviews and publishes them.
+
+## Database and migrations
+
+PostgreSQL `ov_wiki` through `openvibe-sdk/db` (PgBouncer for the app, direct owner role for
+migrations); `migrations/0001..0009` cover spaces/pages/revisions, review kinds, shares, the outbox
+rejection index and defaults. Applied at boot; never edit one that has run. Revision immutability is
+enforced by database triggers, not only by code. Tests use PGlite or `WIKI_TEST_STORE=pg`; a migration
+that a rollback cannot tolerate is not allowed.
+
+## Auth and privacy
+
+Network RS256 JWTs (offline JWKS) and SSO. A person's space role decides writes; services act for a
+person only with `X-OV-Subject` and the capability. Visitors without SSO read public content only; a
+members/private space is gated through VIP and the entitlement check. Cross-site cookie writes are
+refused. AI generates proposals/drafts that need a person's approval before publication; every seed
+page is currently `noindex` because no person has reviewed it, and none is in Search. Public pages are
+cacheable; a signed-in page is `private, no-store`.
+
+## Deploy
+
+`sudo ovhost deploy wiki` (git-checkout: fast-forward `/opt/openvibe.wiki`, install on a lockfile
+change, restart, wait for `/api/ready`; rollback `sudo ovhost rollback wiki --to <sha>`). Unit
+`openvibe-wiki.service` on `127.0.0.1:4800`, env `/etc/openvibe/wiki.env`, database `ov_wiki`, nginx
+`deploy/nginx/openvibe.wiki.conf`; the release migrates at boot.
+
+## Commands and tests
+
+```bash
+npm ci
+npm run dev # http://127.0.0.1:4800
+npm test # every test/*.test.js, temp PGlite + stub upstreams
+npm test -- revisions seo # only matching files
+npm run test:pg # the same on PostgreSQL
+npm run seed # the official space's seed pages
+```
+
+`test/run.js` uses `openvibe-shared/test-runner` (60 s, serial): temporary PGlite, a generated RSA key
+and stub upstreams, never the network. Coverage includes page trees/slugs, immutable revisions and
+diff/revert, blocks/infoboxes/internal links, citations and the citation inspector, Media and
+Community integration, access/VIP/delegation, AI proposals/review, discovery/SEO, account data and the
+empty states.
+
+## Conventions and pitfalls
+
+- CommonJS, 4-space indent, single quotes, semicolons; RFC 9457 problem+json, escaped HTML and a safe
+ block renderer (user content never becomes raw markup).
+- **A revision is immutable.** Publish a new revision; never UPDATE/DELETE a `wiki_page_revisions` row
+ or bypass the trigger. Diff/revert and citations are computed from revisions.
+- **Citations are provenance, not decoration:** store the Sources item id, URL, title, retrieval time
+ and licence; a changed/removed source must be visible as such, never silently rewritten.
+- **Discussion and bytes live elsewhere:** store the Community thread id / Media `med_` id, verify the
+ access with the owning service, and never copy a thread or a file into Wiki.
+- **Seed/AI text is unreviewed until a person approves it:** keep `noindex` and out of Search until
+ reviewed; AI drafts are proposals, not publications.
+- **Slug and tree changes are redirects:** a moved page keeps its canonical identity and leaves a
+ redirect; `[[internal links]]` resolve through the canonical slug.
+- **Search is downstream:** publish/update/remove through `wiki.index_document.*` in the transaction
+ (or the outbox), never write Search's tables.
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@