openvibers/openvibe.blog Commits
README and STATUS.json are current (roadmap WS-U task 3): Capabilities is now a level-2 section and lists the grants Blog uses elsewhere, including network.integration.github.read (changelog) and ai.run.create/ai.run.read (AI drafts), which the grants list lacked; Deploy says production deploys with sudo ovhost deploy blog (unit, port, env file, vhost, rollback) and keeps the first-install steps; Security points to SECURITY.md; Depends on names the current pins (publishing v0.4.0, contracts v0.53.0, shared v1.22.0, sdk v0.12.0); the status line no longer says nothing is published (Patch notes posts are). STATUS.json gains contracts and features, drops the stale VIP and one-post claims, and is updated 2026-09-28.
Changes
@@@ -4,8 +4,9 @@**Status:** alpha (roadmap Wave 16, Blog half). **Public at https://openvibe.blog since 2026-09-23**(the launch release also removed the domain from OpenVibe.Sites). Its capabilities and service--manifest are released in openvibe-contracts v0.18.0. The only post in production is the seed post,--which is still a draft, so nothing is published yet.++manifest are released in openvibe-contracts v0.18.0. The network changelog publishes Patch notes++posts on the official blog ([below](#the-network-changelog-and-patch-notes)); the seed post stays a++draft until a person reviews it.**Domain:** `openvibe.blog` · **Port:** 4810 · **Service id:** `blog`**Plan:** OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §12.6; roadmap §15.13, §29, §32.**License:** AGPL-3.0 (same as every OpenVibe service).@@@ -214,7 +215,7 @@ OpenVibe.Network proxies this feed at `openvibe.network/api/v1/changelog`. Every| `blog.moderation.action` | internal. Staff unpublished (`post.unpublished`) or deleted (`post.deleted`) a post that only their staff powers let them touch, for the network's moderation audit log (ADR-022, `common.moderation-action@1`). Never the content. || `blog.index_document.upserted` / `.deleted` | Documents and tombstones in `search.index-document@1` form, with a monotonic index revision (`createIndexSequencer`). Only published, public, listable posts are upserted; every other state is a tombstone. A post that was never indexed gets no tombstone. |--### Capabilities (released in openvibe-contracts v0.18.0; proposal: `docs/capabilities-proposal/`)++## Capabilities (released in openvibe-contracts v0.18.0; proposal: `docs/capabilities-proposal/`)Service tokens use audience `openvibe.blog`, with one capability per route. The person the serviceacts for goes in `X-OV-Subject`, and membership still applies:@@@ -230,12 +231,19 @@ with the contracts library's matching rule (`server/auth/capabilities.js`). The(proposal: `docs/service-manifest-proposal.json`) is released in openvibe-contracts v0.18.0; thisrepo pins v0.33.0.++Called elsewhere, as the service principal `blog`: `identity.subject.resolve` and++`network.integration.github.read` (Network), `events.event.publish` (Events),++`community.comment.write` and optionally `community.comment.moderate` (Community),++`media.object.read` (Media), `vip.resource.policy.evaluate` (VIP) and `ai.run.create` with++`ai.run.read` (AI, for AI drafts). The list with audiences is under++[Grants the Network must hold for client `blog`](#grants-the-network-must-hold-for-client-blog).++## Depends on--- **Packages** (all pinned by release tarball): `openvibe-publishing` v0.2.1 (revisions, schedule,++- **Packages** (all pinned by release tarball): `openvibe-publishing` v0.4.0 (revisions, schedule,taxonomy, citations, media, discussion, seo, authorship, index-hooks, ssr), `openvibe-contracts`-- v0.33.0, `openvibe-shared` v1.5.1 (chrome, app icon, footer, legal, release, metrics, ready,-- theme presets), `openvibe-sdk` v0.5.0 (events outbox, service tokens).++ v0.53.0, `openvibe-shared` v1.22.0 (chrome, app icon, footer, legal, release, metrics, ready,++ theme presets), `openvibe-sdk` v0.12.0 (events outbox, service tokens, per-actor limits).- **OpenVibe.Network:**- SSO: the OAuth client `blog` is already seeded with redirect`https://openvibe.blog/auth/callback`.@@@ -261,6 +269,10 @@ Each grant is `[client, capability, audience]`:- `[blog, media.object.read, openvibe.media]`, namespace `blog`. Media also needs a `blog` tenant.- `[blog, vip.resource.policy.evaluate, openvibe.vip]` for members-only posts (until granted, only theblog's members and staff read them).++- `[blog, network.integration.github.read, openvibe.network]` (optional: the changelog reads GitHub++ with the network's token instead of anonymously; `CHANGELOG_GITHUB_TOKEN` overrides it).++- `[blog, ai.run.create, openvibe.ai]` and `[blog, ai.run.read, openvibe.ai]` for AI drafts (the++ editor's "draft with AI" and the dev-blog draft of each patch notes post).- For OpenVibe.AI to deliver drafts: `[ai, blog.post.create, openvibe.blog]`. Add`[ai, blog.post.read, openvibe.blog]` if it reads the drafts back.@@@ -324,6 +336,8 @@ its commit or tag, date and GitHub URL, and is attached as a citation.## Security and threat review++Reporting a vulnerability: [SECURITY.md](SECURITY.md).++- **Identity:**- Only verified Network JWTs (offline RS256 against JWKS) and service tokens for audience`openvibe.blog`.@@@ -396,6 +410,16 @@ fnm exec --using=22.22.1 npm run dev # http://localhost:4810 (set OV_OAUTH## Deploy (for the lead)++Production deploys with `sudo ovhost deploy blog` on the host (strategy `git-checkout`: fetch,++fast-forward `/opt/openvibe.blog`, install on a lockfile change, restart, wait for `/api/ready`).++The unit is `openvibe-blog.service` on `127.0.0.1:4810`, the env file `/etc/openvibe/blog.env`. nginx serves++`openvibe.blog` from [deploy/nginx/openvibe.blog.conf](deploy/nginx/openvibe.blog.conf).++Rollback: ovhost puts the previous sha back by itself when `/api/ready` does not answer 2xx after the++restart; afterwards `sudo ovhost rollback blog --to <sha>`. Nothing blocks a rollback: the schema++code only adds tables and columns.++++First install (done once; kept for a rebuild):++1. **Code and config:**- Put the code at `/opt/openvibe.blog` and run `npm ci --omit=dev` on Node 22.- Create `/etc/openvibe/blog.env` (0600) from `.env.example`. Set these values:@@@ -7,12 +7,8 @@"runtime": "node 22, express 4, better-sqlite3 11","port": 4810,"domain": "openvibe.blog",-- "packages": [-- "openvibe-publishing v0.2.1",-- "openvibe-contracts v0.33.0",-- "openvibe-shared v1.5.1",-- "openvibe-sdk v0.5.0"-- ],++ "contracts": "openvibe-contracts v0.53.0",++ "packages": ["openvibe-publishing v0.4.0", "openvibe-contracts v0.53.0", "openvibe-shared v1.22.0", "openvibe-sdk v0.12.0"],"capabilities": ["blog.blog.create","blog.blog.configure",@@@ -29,11 +25,27 @@],"capabilitiesRegistered": true,"serviceManifestRegistered": true,-- "entitlements": "OpenVibe.VIP in code (policies/evaluate with the blog owner and the blog:gated_post default gate, cached 30 s; teaser and join link otherwise). Not deployed yet, and the Network has not granted blog vip.resource.policy.evaluate: until both, members-only posts admit only the blog's members and staff",-- "seedPost": "draft in production (verbatim quotes; not reviewed by a person); 0 posts published",++ "entitlements": "OpenVibe.VIP in code (policies/evaluate with the blog owner and the blog:gated_post default gate, cached 30 s; teaser and join link otherwise). It needs the Network grant [blog, vip.resource.policy.evaluate, openvibe.vip]; without it, or when VIP is unreachable, members-only posts admit only the blog's members and staff (the gate fails closed)",++ "seedPost": "a draft (verbatim quotes; not reviewed by a person)","deployed": true,-- "deployment": "Public at https://openvibe.blog since 2026-09-23 (~03:00 UTC): /opt/openvibe.blog at e7b4986, unit openvibe-blog on 127.0.0.1:4810 behind the openvibe.blog nginx vhost, principal blog. Production content: 1 blog (official), 1 post, which is a draft, so the public site shows nothing published yet. Restore drill passed 2026-09-23.",++ "deployment": "Public at https://openvibe.blog since 2026-09-23 (~03:00 UTC): deployed with `sudo ovhost deploy blog`, /opt/openvibe.blog, unit openvibe-blog on 127.0.0.1:4810 behind the openvibe.blog nginx vhost, principal blog. The network changelog publishes Patch notes posts on the official blog. Restore drill passed 2026-09-23.","placeholderInSites": false,-- "updated": "2026-09-23",-- "deployedAt": "2026-09-23"++ "updated": "2026-09-28",++ "deployedAt": "2026-09-23",++ "features": [++ "the official blog and a blog for every member, with owners, editors and authors",++ "drafts and immutable revisions with conflict detection, diff and revert",++ "publish, unpublish, soft delete and scheduled publishing",++ "tags, categories, series and 301s on slug changes",++ "Media-backed covers, inline figures and galleries",++ "six theme presets",++ "Community comment threads",++ "AI drafts that need a person's review",++ "members-only posts gated by OpenVibe.VIP (fails closed)",++ "network changelog feed and Patch notes posts",++ "feeds, sitemaps and server-rendered pages useful without JavaScript",++ "/api/v1 by capability",++ "SDK outbox events and Search index documents",++ "per-actor limits (openvibe-sdk/limits)"++ ]}
@@ -4,8 +4,9 @@
@@ -4,8 +4,9 @@
**Status:** alpha (roadmap Wave 16, Blog half). **Public at https://openvibe.blog since 2026-09-23**
**Status:** alpha (roadmap Wave 16, Blog half). **Public at https://openvibe.blog since 2026-09-23**
(the launch release also removed the domain from OpenVibe.Sites). Its capabilities and service
(the launch release also removed the domain from OpenVibe.Sites). Its capabilities and service
-manifest are released in openvibe-contracts v0.18.0. The only post in production is the seed post,
-which is still a draft, so nothing is published yet.
+manifest are released in openvibe-contracts v0.18.0. The network changelog publishes Patch notes
+posts on the official blog ([below](#the-network-changelog-and-patch-notes)); the seed post stays a
+draft until a person reviews it.
**Domain:** `openvibe.blog` · **Port:** 4810 · **Service id:** `blog`
**Domain:** `openvibe.blog` · **Port:** 4810 · **Service id:** `blog`
**Plan:** OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §12.6; roadmap §15.13, §29, §32.
**Plan:** OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §12.6; roadmap §15.13, §29, §32.
**License:** AGPL-3.0 (same as every OpenVibe service).
**License:** AGPL-3.0 (same as every OpenVibe service).
@@ -214,7 +215,7 @@ OpenVibe.Network proxies this feed at `openvibe.network/api/v1/changelog`. Every
@@ -214,7 +215,7 @@ OpenVibe.Network proxies this feed at `openvibe.network/api/v1/changelog`. Every
| `blog.moderation.action` | internal. Staff unpublished (`post.unpublished`) or deleted (`post.deleted`) a post that only their staff powers let them touch, for the network's moderation audit log (ADR-022, `common.moderation-action@1`). Never the content. |
| `blog.moderation.action` | internal. Staff unpublished (`post.unpublished`) or deleted (`post.deleted`) a post that only their staff powers let them touch, for the network's moderation audit log (ADR-022, `common.moderation-action@1`). Never the content. |
| `blog.index_document.upserted` / `.deleted` | Documents and tombstones in `search.index-document@1` form, with a monotonic index revision (`createIndexSequencer`). Only published, public, listable posts are upserted; every other state is a tombstone. A post that was never indexed gets no tombstone. |
| `blog.index_document.upserted` / `.deleted` | Documents and tombstones in `search.index-document@1` form, with a monotonic index revision (`createIndexSequencer`). Only published, public, listable posts are upserted; every other state is a tombstone. A post that was never indexed gets no tombstone. |
-### Capabilities (released in openvibe-contracts v0.18.0; proposal: `docs/capabilities-proposal/`)
+## Capabilities (released in openvibe-contracts v0.18.0; proposal: `docs/capabilities-proposal/`)
Service tokens use audience `openvibe.blog`, with one capability per route. The person the service
Service tokens use audience `openvibe.blog`, with one capability per route. The person the service
acts for goes in `X-OV-Subject`, and membership still applies:
acts for goes in `X-OV-Subject`, and membership still applies:
@@ -230,12 +231,19 @@ with the contracts library's matching rule (`server/auth/capabilities.js`). The
@@ -230,12 +231,19 @@ with the contracts library's matching rule (`server/auth/capabilities.js`). The
(proposal: `docs/service-manifest-proposal.json`) is released in openvibe-contracts v0.18.0; this
(proposal: `docs/service-manifest-proposal.json`) is released in openvibe-contracts v0.18.0; this
repo pins v0.33.0.
repo pins v0.33.0.
+Called elsewhere, as the service principal `blog`: `identity.subject.resolve` and
+`network.integration.github.read` (Network), `events.event.publish` (Events),
+`community.comment.write` and optionally `community.comment.moderate` (Community),
+`media.object.read` (Media), `vip.resource.policy.evaluate` (VIP) and `ai.run.create` with
+`ai.run.read` (AI, for AI drafts). The list with audiences is under
+[Grants the Network must hold for client `blog`](#grants-the-network-must-hold-for-client-blog).
+
## Depends on
## Depends on
-- **Packages** (all pinned by release tarball): `openvibe-publishing` v0.2.1 (revisions, schedule,
+- **Packages** (all pinned by release tarball): `openvibe-publishing` v0.4.0 (revisions, schedule,
taxonomy, citations, media, discussion, seo, authorship, index-hooks, ssr), `openvibe-contracts`
taxonomy, citations, media, discussion, seo, authorship, index-hooks, ssr), `openvibe-contracts`
- v0.33.0, `openvibe-shared` v1.5.1 (chrome, app icon, footer, legal, release, metrics, ready,
- theme presets), `openvibe-sdk` v0.5.0 (events outbox, service tokens).
+ v0.53.0, `openvibe-shared` v1.22.0 (chrome, app icon, footer, legal, release, metrics, ready,
+ theme presets), `openvibe-sdk` v0.12.0 (events outbox, service tokens, per-actor limits).
- **OpenVibe.Network:**
- **OpenVibe.Network:**
- SSO: the OAuth client `blog` is already seeded with redirect
- SSO: the OAuth client `blog` is already seeded with redirect
`https://openvibe.blog/auth/callback`.
`https://openvibe.blog/auth/callback`.
@@ -261,6 +269,10 @@ Each grant is `[client, capability, audience]`:
@@ -261,6 +269,10 @@ Each grant is `[client, capability, audience]`:
- `[blog, media.object.read, openvibe.media]`, namespace `blog`. Media also needs a `blog` tenant.
- `[blog, media.object.read, openvibe.media]`, namespace `blog`. Media also needs a `blog` tenant.
- `[blog, vip.resource.policy.evaluate, openvibe.vip]` for members-only posts (until granted, only the
- `[blog, vip.resource.policy.evaluate, openvibe.vip]` for members-only posts (until granted, only the
blog's members and staff read them).
blog's members and staff read them).
+- `[blog, network.integration.github.read, openvibe.network]` (optional: the changelog reads GitHub
+ with the network's token instead of anonymously; `CHANGELOG_GITHUB_TOKEN` overrides it).
+- `[blog, ai.run.create, openvibe.ai]` and `[blog, ai.run.read, openvibe.ai]` for AI drafts (the
+ editor's "draft with AI" and the dev-blog draft of each patch notes post).
- For OpenVibe.AI to deliver drafts: `[ai, blog.post.create, openvibe.blog]`. Add
- For OpenVibe.AI to deliver drafts: `[ai, blog.post.create, openvibe.blog]`. Add
`[ai, blog.post.read, openvibe.blog]` if it reads the drafts back.
`[ai, blog.post.read, openvibe.blog]` if it reads the drafts back.
@@ -324,6 +336,8 @@ its commit or tag, date and GitHub URL, and is attached as a citation.
@@ -324,6 +336,8 @@ its commit or tag, date and GitHub URL, and is attached as a citation.
## Security and threat review
## Security and threat review
+Reporting a vulnerability: [SECURITY.md](SECURITY.md).
+
- **Identity:**
- **Identity:**
- Only verified Network JWTs (offline RS256 against JWKS) and service tokens for audience
- Only verified Network JWTs (offline RS256 against JWKS) and service tokens for audience
`openvibe.blog`.
`openvibe.blog`.
@@ -396,6 +410,16 @@ fnm exec --using=22.22.1 npm run dev # http://localhost:4810 (set OV_OAUTH
@@ -396,6 +410,16 @@ fnm exec --using=22.22.1 npm run dev # http://localhost:4810 (set OV_OAUTH
## Deploy (for the lead)
## Deploy (for the lead)
+Production deploys with `sudo ovhost deploy blog` on the host (strategy `git-checkout`: fetch,
+fast-forward `/opt/openvibe.blog`, install on a lockfile change, restart, wait for `/api/ready`).
+The unit is `openvibe-blog.service` on `127.0.0.1:4810`, the env file `/etc/openvibe/blog.env`. nginx serves
+`openvibe.blog` from [deploy/nginx/openvibe.blog.conf](deploy/nginx/openvibe.blog.conf).
+Rollback: ovhost puts the previous sha back by itself when `/api/ready` does not answer 2xx after the
+restart; afterwards `sudo ovhost rollback blog --to <sha>`. Nothing blocks a rollback: the schema
+code only adds tables and columns.
+
+First install (done once; kept for a rebuild):
+
1. **Code and config:**
1. **Code and config:**
- Put the code at `/opt/openvibe.blog` and run `npm ci --omit=dev` on Node 22.
- Put the code at `/opt/openvibe.blog` and run `npm ci --omit=dev` on Node 22.
- Create `/etc/openvibe/blog.env` (0600) from `.env.example`. Set these values:
- Create `/etc/openvibe/blog.env` (0600) from `.env.example`. Set these values:
@@ -7,12 +7,8 @@
@@ -7,12 +7,8 @@
"runtime": "node 22, express 4, better-sqlite3 11",
"runtime": "node 22, express 4, better-sqlite3 11",
"port": 4810,
"port": 4810,
"domain": "openvibe.blog",
"domain": "openvibe.blog",
- "packages": [
- "openvibe-publishing v0.2.1",
- "openvibe-contracts v0.33.0",
- "openvibe-shared v1.5.1",
- "openvibe-sdk v0.5.0"
- ],
+ "contracts": "openvibe-contracts v0.53.0",
+ "packages": ["openvibe-publishing v0.4.0", "openvibe-contracts v0.53.0", "openvibe-shared v1.22.0", "openvibe-sdk v0.12.0"],
"capabilities": [
"capabilities": [
"blog.blog.create",
"blog.blog.create",
"blog.blog.configure",
"blog.blog.configure",
@@ -29,11 +25,27 @@
@@ -29,11 +25,27 @@
],
],
"capabilitiesRegistered": true,
"capabilitiesRegistered": true,
"serviceManifestRegistered": true,
"serviceManifestRegistered": true,
- "entitlements": "OpenVibe.VIP in code (policies/evaluate with the blog owner and the blog:gated_post default gate, cached 30 s; teaser and join link otherwise). Not deployed yet, and the Network has not granted blog vip.resource.policy.evaluate: until both, members-only posts admit only the blog's members and staff",
- "seedPost": "draft in production (verbatim quotes; not reviewed by a person); 0 posts published",
+ "entitlements": "OpenVibe.VIP in code (policies/evaluate with the blog owner and the blog:gated_post default gate, cached 30 s; teaser and join link otherwise). It needs the Network grant [blog, vip.resource.policy.evaluate, openvibe.vip]; without it, or when VIP is unreachable, members-only posts admit only the blog's members and staff (the gate fails closed)",
+ "seedPost": "a draft (verbatim quotes; not reviewed by a person)",
"deployed": true,
"deployed": true,
- "deployment": "Public at https://openvibe.blog since 2026-09-23 (~03:00 UTC): /opt/openvibe.blog at e7b4986, unit openvibe-blog on 127.0.0.1:4810 behind the openvibe.blog nginx vhost, principal blog. Production content: 1 blog (official), 1 post, which is a draft, so the public site shows nothing published yet. Restore drill passed 2026-09-23.",
+ "deployment": "Public at https://openvibe.blog since 2026-09-23 (~03:00 UTC): deployed with `sudo ovhost deploy blog`, /opt/openvibe.blog, unit openvibe-blog on 127.0.0.1:4810 behind the openvibe.blog nginx vhost, principal blog. The network changelog publishes Patch notes posts on the official blog. Restore drill passed 2026-09-23.",
"placeholderInSites": false,
"placeholderInSites": false,
- "updated": "2026-09-23",
- "deployedAt": "2026-09-23"
+ "updated": "2026-09-28",
+ "deployedAt": "2026-09-23",
+ "features": [
+ "the official blog and a blog for every member, with owners, editors and authors",
+ "drafts and immutable revisions with conflict detection, diff and revert",
+ "publish, unpublish, soft delete and scheduled publishing",
+ "tags, categories, series and 301s on slug changes",
+ "Media-backed covers, inline figures and galleries",
+ "six theme presets",
+ "Community comment threads",
+ "AI drafts that need a person's review",
+ "members-only posts gated by OpenVibe.VIP (fails closed)",
+ "network changelog feed and Patch notes posts",
+ "feeds, sitemaps and server-rendered pages useful without JavaScript",
+ "/api/v1 by capability",
+ "SDK outbox events and Search index documents",
+ "per-actor limits (openvibe-sdk/limits)"
+ ]
}
}