# OpenVibe.Publishing > Shared publishing packages for the OpenVibe publication products: Wiki, Blog, News, Reviews, > Deals, Coupons and Trade. **Docs:** https://openvibe.services · **For AI:** https://openvibe.network/llms.txt **Install and test:** `npm ci && npm test` (Node 20 or newer) **Status:** alpha, **1.7.1**: the stores run on PostgreSQL through the `openvibe-sdk/db` async data layer (ADR-035: PostgreSQL 18 behind PgBouncer in production, PGlite in tests). Every store method is async, takes the product's `openvibe-sdk/db` handle, and accepts the caller's transaction handle; each store gives its DDL as `schema(prefix)` for the product's migrations. v1.1.0 adds the shared ingest chassis (`openvibe-publishing/ingest`) and publication glue (`openvibe-publishing/publication`) — additive, no existing export changed. v1.2.0 adds `openvibe-publishing/layout`, the page document the seven sites render through `openvibe-shared/shell` (additive). v1.3.0 forwards the AI summary, facts, `updated` and `url` through `layout.renderDocument` to the shell (additive). v1.4.0 adds `openvibe-publishing/search-feed`, which puts a record product's pages in OpenVibe.Search (additive). v1.4.1 keeps a request path that starts with several slashes (`//feed/`) on the site in `seo.canonicalUrl`, where it threw (a 500 on News, Deals and Blog). v1.5.0 adds `openvibe-publishing/search-relay`, which delivers the Search documents a product queued in its own outbox table through Search's owner API (Meme, Exchange and Fun queue them; additive). The exit proof (`examples/two-products`) runs in `npm test`. v1.6.0 adds `openvibe-publishing/blocks`, the rich block model Blog and Wiki share: a body is Markdown plus blocks, parsed, validated, rendered server-side and flattened to text (additive). Releases v0.1.0 to v1.7.1 are tagged (see [CHANGELOG.md](CHANGELOG.md)). Fifteen products pin a release. The seven publication products run on PostgreSQL and are public: openvibe.wiki, openvibe.blog, openvibe.news, openvibe.reviews, openvibe.deals, openvibe.coupons and openvibe.trade (on 2026-10-22 Blog, News and Deals pinned v1.4.1, the other four v1.3.0). Seven more use `search-feed` to put their pages in OpenVibe.Search: Work, Rent, Help, Inventory, Quest, Host and OpenVibe.Games (its creator worlds); Food uses the AI draft client. Each adopts a release by pinning its tag, and OpenVibe.Contracts' `docs/ESTATE.md` lists every pin. **Package:** `openvibe-publishing` (CommonJS, Node ≥ 20, production runs Node 22). **License:** MIT, like OpenVibe.Shared. ## Purpose One implementation of how the publication products publish: revisions, scheduling, taxonomy, citations, media references, discussion references, the indexability gate, feeds and sitemaps, authorship and AI disclosure, Search documents and server-rendered HTML. Each product keeps its own state and rules; this repository keeps the code they share. ## Owns - the modules below and their table layouts (the DDL each gives as `schema(prefix)`), with the product's prefix, inside the product's own database - the indexability gate's rules and its stable reason codes (`seo.REASONS`) - the shape of the documents and envelopes the products send to OpenVibe.Search and OpenVibe.Events It owns no state: no runtime, port, domain or database of its own (next section). ## The rule: packages, not an authority This repository is **code, never state**. It has no runtime, no port, no domain, no database and no API of its own, and it must never get one. Each product (Wiki, Blog, News, …) owns its publication state in its own database; these modules take the product's `openvibe-sdk/db` handle and a table prefix, and use `_…` tables *inside that product's database* (the product's migrations create them from `schema(prefix)`). Two products share the code, not the data: the wiki's database holds `wiki_*` tables, the blog's holds `blog_*` tables, and nothing is shared between them (proved by [test/two-products.test.js](test/two-products.test.js)). Following the plan (§12.4, §31.1, §31.5): share *how* something is done (revisions, the gate, feeds, indexing); never share *what is true* (which revision is live, who may read it, editorial rules). A generic `OpenVibe.Content` authority is explicitly not what this is. ## Module map Every module is its own entry point and can be used alone. | Entry point | What it does | Tables (in the consumer's DB) | |---|---|---| | `openvibe-publishing/revisions` | Drafts, immutable revisions with parent pointers, optimistic concurrency (`expectedRevision` → 412 `revision.conflict`), line/word diff, revert as a new revision, audited purge | `_revisions`, `_drafts`, `_revision_purges` | | `openvibe-publishing/schedule` | Scheduled publish/unpublish jobs: idempotent `schedule()`, lease-based `claim()`, re-run safe across worker restarts, retries with backoff, injectable clock | `_schedule_jobs` | | `openvibe-publishing/taxonomy` | Tags and terms with slugs, hierarchical categories (ancestors for breadcrumbs, descendants, trees, cycle checks) | `_terms`, `_term_links` | | `openvibe-publishing/citations` | Source references on one revision: Sources item id and/or URL, `retrieved_at`, quote span, license note; append-only, never dropped by later revisions; `carryForward` to reuse | `_citations`, `_citation_purges` | | `openvibe-publishing/media` | Attachments by Media object id (`med_…` / `legacy:…`), explicit `broken` state when the object is gone, `verify()` that never flips state on an outage, `
` rendering with an honest placeholder | `_attachments` | | `openvibe-publishing/discussion` | Community comment-thread references: resolves via `POST /api/v1/comments/threads/resolve`, stores the thread id only (no comment content) | `_discussion_refs` | | `openvibe-publishing/seo` | The deterministic **indexability gate**; canonical URLs; history-aware redirects (old slug → 301, gone → 410); meta/robots tags; sitemaps; RSS, Atom and JSON Feed; JSON-LD built only from provided fields | `_redirects` | | `openvibe-publishing/authorship` | human / ai / hybrid / imported records with the OpenVibe.AI workflow + run id, disclosure labels, AI content held as draft + noindex until a person's review | `_reviews` | | `openvibe-publishing/index-hooks` | `search.index-document@1` documents and tombstones, the `.index_document.upserted\|deleted` events OpenVibe.Search consumes, a monotonic index-revision sequencer, and the product's own `..published\|updated\|unpublished\|deleted` events | `_index_revisions` | | `openvibe-publishing/ai` | Ask OpenVibe.AI for a draft: run a registered workflow with the product's service token (polls a slow run), read its citations, get the `{ id, version, runId, model }` an AI authorship record needs; coded `AiRunError`s, never a partial draft | — | | `openvibe-publishing/ssr` | auto-escaping `html` tagged templates with `raw()`, a safe Markdown subset, plain-text extraction, word count, server pagination, breadcrumbs, diff markup, honest `