openvibers/openvibe.tips Commits
openvibe-shared v1.5.0 (Track R release manifest), openvibe-contracts v0.32.0, openvibe-sdk v0.5.0
Changes
@@@ -41,7 +41,7 @@ from *interaction delivered*.(openvibe-sdk transactional outbox)- **OpenVibe.Network** — SSO for people, service tokens, JWKS, identity resolve (importer)- **OpenVibe.Live** — chat delivery through `/internal/tips/deliveries` (deployed in Live since `f11f809`; [docs/live-patch.diff](docs/live-patch.diff) is the original patch) until OpenVibe.Chat exposes paid messages; overlay consumer--- **OpenVibe.Shared** v1.3.0 (app icon, SSR footer, noscript nav, release manifest, legal pages) and++- **OpenVibe.Shared** v1.5.0 (app icon, SSR footer, noscript nav, release manifest, legal pages) andthe Network's `navbar.js`## Run it@@@ -121,9 +121,9 @@ token as a Bearer and act on their own things only. The API never reads cookies.| `GET /goals?creator=`, `GET /goals/:id` | `tips.goal.update` (private; full view) | public shape when the page is on (the creator's page settings); owner sees contributions || `GET /profiles/:creator/supporters` | `tips.profile.get` (while not public) | public when the creator shows the supporters page; the owner || `GET /me/export`, `POST /me/erase` | — (people only) | the supporter's own tips: download; erase their data from them |--| `GET /moderation?creator=&state=held\|hidden\|visible\|all` | `tips.interaction.moderate` (proposed) | the creator and their moderators |--| `POST /interactions/:id/hide`, `POST /interactions/:id/restore` `{reason?}` | `tips.interaction.moderate` (proposed) | the creator and their moderators |--| `GET /moderation/log?creator=` | `tips.interaction.moderate` (proposed) | owner |++| `GET /moderation?creator=&state=held\|hidden\|visible\|all` | `tips.interaction.moderate` | the creator and their moderators |++| `POST /interactions/:id/hide`, `POST /interactions/:id/restore` `{reason?}` | `tips.interaction.moderate` | the creator and their moderators |++| `GET /moderation/log?creator=` | `tips.interaction.moderate` | owner || `GET\|POST /moderators`, `POST /moderators/:subject/remove` | `tips.profile.update` | owner || `POST /goals`, `PATCH /goals/:id`, `POST /goals/:id/close` | `tips.goal.create` / `.update` / `.close` | owner || `GET\|POST /overlay-tokens`, `POST /overlay-tokens/:id/revoke` | `tips.overlay.token.create` / `.revoke` | owner |@@@ -131,15 +131,16 @@ token as a Bearer and act on their own things only. The API never reads cookies.| `POST /simulate` | `tips.simulation.run` | owner || `POST /internal/events` | Events webhook signature (`TIPS_EVENTS_SECRET`), loopback only | — |--`tips.interaction.moderate` is proposed in [docs/capabilities-proposal/](docs/capabilities-proposal/) and--not in openvibe-contracts yet (grants are matched by string, so a Network grant of it works today).++`tips.interaction.moderate` was proposed in [docs/capabilities-proposal/](docs/capabilities-proposal/)++and is registered in openvibe-contracts v0.32.0.The capabilities and the service manifest were released in openvibe-contracts v0.15.0 (3-segment ids:the charter's `tips.simulate` is `tips.simulation.run`; `tips.interaction.record` is new, for EXTERNALtips); the drafts stay in [docs/capabilities-proposal/](docs/capabilities-proposal/) and[docs/service-manifest-proposal.json](docs/service-manifest-proposal.json). Grants are matched with--contracts' `capabilities.grants()` (exact id or a `.*` family). Tips pins openvibe-contracts v0.30.2,--which also carries the payload schemas of the six `tips.*` events below; `test/contracts.test.js`--validates every envelope and payload Tips produces against them.++contracts' `capabilities.grants()` (exact id or a `.*` family). Tips pins openvibe-contracts v0.32.0,++which also carries the payload schemas of the six `tips.*` events below (v0.30.2) and of++`tips.interaction.moderated|erased` (v0.32.0); `test/contracts.test.js` validates every envelope and++payload Tips produces against them.## Privacy@@@ -203,8 +204,8 @@ toward its goal and the creator's totals.Produced through the openvibe-sdk outbox (table `event_outbox`, source `tips`, same transaction as thechange; relayed when `EVENTS_URL` and the client secret are set): `tips.interaction.ready`,`tips.interaction.failed`, `tips.interaction.cancelled`, `tips.goal.updated`, `tips.overlay.delivered`,--`tips.overlay.failed`, and two whose schemas are proposed in [docs/events-proposal/](docs/events-proposal/)--(not yet in openvibe-contracts): `tips.interaction.erased` (a supporter's erasure; `{ interaction_id,++`tips.overlay.failed`, and two whose schemas were proposed in [docs/events-proposal/](docs/events-proposal/)++and are in openvibe-contracts since v0.32.0: `tips.interaction.erased` (a supporter's erasure; `{ interaction_id,creator, erased_at, redacts }`) and `tips.interaction.moderated` (`{ interaction_id, creator, action:filtered|held|hidden|restored, by: filter|creator|moderator|service, moderation_state, cancelled_effects }`).Simulations and Billing test money produce none. An anonymous supporter is never named in an event.@@@ -336,7 +337,7 @@ exist here (plan §12.12):4. real persistence and end-to-end workflows — ✔ against stubs; not yet against the real Billing andEvents (Billing is in shadow and has sent no events; the production database is empty; the Liveimport has not been run);--5. capability and event registration against OpenVibe.Contracts — ✔ v0.15.0, payload schemas in v0.30.2 (pinned);++5. capability and event registration against OpenVibe.Contracts — ✔ v0.15.0, payload schemas in v0.30.2 and v0.32.0 (pinned);6. a migration/seed strategy ✔, a written threat review ✔ ([docs/threat-review.md](docs/threat-review.md),internal; an independent review is still due), and sitemap/robots ✔ (no feed);7. acceptance tests proving the advertised functionality — ✔ (table above).@@@ -7,6 +7,6 @@"adr": "ADR-012","authoritative": false,"deployment": "Deployed internally, not launched: openvibe-ovh /opt/openvibe.tips (release 435f32b), unit openvibe-tips, 127.0.0.1:4610 loopback only; https://openvibe.tips still serves the OpenVibe.Sites placeholder. Principal tips; Events subscription billing.transaction.* -> /internal/events; TIPS_CHAT_ADAPTER=none. Pins openvibe-contracts v0.15.0, openvibe-sdk v0.4.0, openvibe-shared v1.2.1.",-- "notes": "Runtime, API, SSR pages, overlays, Billing settlement consumer, events outbox, Live import and reconciliation are built and tested against stubs. Capabilities (17 tips.*) and the service manifest are registered in OpenVibe.Contracts v0.15.0; the six tips.* event payload schemas are in v0.30.2, which the repository pins since 2026-09-23 (production still runs v0.15.0). Production database is empty (0 profiles, interactions, goals, overlay tokens); the Live import (scripts/import-live.js) has never been run; 0 billing events have ever arrived because Billing runs in shadow. Live's /internal/tips/deliveries route is deployed (Live f11f809) but never called. The restore drill passed on the host 2026-09-23 05:23 UTC. Built since (not deployed yet): /metrics (loopback only) and the Billing/Events checks in /api/ready. Built since (not deployed yet): supporter privacy (anonymous, hidden amount, private message) across API, overlays, chat jobs, pages and events; the creator's public goal and supporters page settings; a supporter's export and erasure (tips.interaction.erased, schema proposed in docs/events-proposal/, not yet in openvibe-contracts). Moderation (not deployed yet): the creator's word filter (mask or hold for review) before anything is shown or read, hide/show by the creator, moderators (invitation links) or services, tips.interaction.moderated (schema and the tips.interaction.moderate capability proposed in docs/, not yet in openvibe-contracts). A written internal threat review (docs/threat-review.md) with its fixes and test/security.test.js. Not done: an independent security review; the openvibe.tips launch. Live stays the place tips happen until the Billing cutover (blocked on the owner re-pointing the PowerChat webhook) and the launch rule in README.md holds.",++ "notes": "Runtime, API, SSR pages, overlays, Billing settlement consumer, events outbox, Live import and reconciliation are built and tested against stubs. Capabilities (17 tips.*) and the service manifest are registered in OpenVibe.Contracts v0.15.0; the six tips.* event payload schemas are in v0.30.2 and tips.interaction.moderated|erased in v0.32.0, which the repository pins since 2026-09-23 (production still runs v0.15.0). Production database is empty (0 profiles, interactions, goals, overlay tokens); the Live import (scripts/import-live.js) has never been run; 0 billing events have ever arrived because Billing runs in shadow. Live's /internal/tips/deliveries route is deployed (Live f11f809) but never called. The restore drill passed on the host 2026-09-23 05:23 UTC. Built since (not deployed yet): /metrics (loopback only) and the Billing/Events checks in /api/ready. Built since (not deployed yet): supporter privacy (anonymous, hidden amount, private message) across API, overlays, chat jobs, pages and events; the creator's public goal and supporters page settings; a supporter's export and erasure (tips.interaction.erased, schema in openvibe-contracts v0.32.0). Moderation (not deployed yet): the creator's word filter (mask or hold for review) before anything is shown or read, hide/show by the creator, moderators (invitation links) or services, tips.interaction.moderated (schema and the tips.interaction.moderate capability in openvibe-contracts v0.32.0). A written internal threat review (docs/threat-review.md) with its fixes and test/security.test.js. Not done: an independent security review; the openvibe.tips launch. Live stays the place tips happen until the Billing cutover (blocked on the owner re-pointing the PowerChat webhook) and the launch rule in README.md holds.","updated": "2026-09-23"}@@@ -14,9 +14,9 @@"dotenv": "^16.4.7","express": "^4.21.2","jsonwebtoken": "^9.0.2",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.30.2",-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.4.0",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0"++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0"},"engines": {"node": ">=22.0.0"@@@ -983,9 +983,9 @@}},"node_modules/openvibe-contracts": {-- "version": "0.30.2",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.30.2",-- "integrity": "sha512-R01NgRY5V/GEYWkWUyxfl1MifSQb4dfJy11VS9RP+wsWedh2Crr9/IL7yGSn1PytwTKKL+tQl/r4ppGfH3TwLg==",++ "version": "0.32.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",++ "integrity": "sha512-ntN58SIG03YDxbhsiIzOM5GRH19jRMHZUYWI3xU2VwovExagF5DdwApLVfezsu84UTJ+V2Jfn+LWDc+Oz8ab+g==","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -999,18 +999,18 @@}},"node_modules/openvibe-sdk": {-- "version": "0.4.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.4.0",-- "integrity": "sha512-Hyz55YOMIs0xwCQ1NXsN0sUYhlbKXAX3uWY2fPCdEEL18FXUYVhK8wlG/5JMMypFHnjDiSd9VHzBNG12hWsmkQ==",++ "version": "0.5.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",++ "integrity": "sha512-QVJkR5iazQX0ZwIrDp2A88ZeYYBSXEmSzhpgURPUSnzezIM3jz0HBhVKowyhkvg2aBO61XNf77eb4zJGkMjZiw==","license": "MIT","engines": {"node": ">=20"}},"node_modules/openvibe-shared": {-- "version": "1.3.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0",-- "integrity": "sha512-w8yl/5+9eend8I2O/DzAJnEAL2N3GTUY7c7pzK+MLJuLAGD8FmJJQ9gwbgyrq70Snjt3ebrg3TlpORhRAp2kgA==",++ "version": "1.5.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0",++ "integrity": "sha512-cehnscuYMkJwuCYGcI6wPSswbUn0bhTyGAaD5feNCoO//2e9gyC7cgjkEcIOtM8DQF3yTuUNyXnOwckIeM4fUg==","license": "MIT","dependencies": {"jsonwebtoken": "^9.0.0"@@@ -21,9 +21,9 @@"dotenv": "^16.4.7","express": "^4.21.2","jsonwebtoken": "^9.0.2",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.30.2",-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.4.0",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0"++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0"},"engines": {"node": ">=22.0.0"@@@ -61,7 +61,7 @@ const CAP = {configGet: 'tips.overlay.config.get',configUpdate: 'tips.overlay.config.update',simulate: 'tips.simulation.run',-- moderate: 'tips.interaction.moderate', // proposed (docs/capabilities-proposal/), not yet in openvibe-contracts++ moderate: 'tips.interaction.moderate', // in openvibe-contracts since v0.32.0 (was docs/capabilities-proposal/)};function v1Router({ domain, apiAuth }) {@@@ -82,7 +82,8 @@ function createApp(opts = {}) {// Events are optional and degrade it (see observability.js).const readiness = createTipsReadiness({ db, keys, config, outbox, release: release.release, fetchImpl });app.get('/api/ready', readiness.handler);-- app.get('/release.json', release.handler);++ // GET /release.json (ADR-016) and POST /release-metrics: open tabs' update reports into /metrics.++ release.mount(app, { registry: metrics.registry });const consumer = consumerRouter({ domain, config, log });// Service-to-service only: OpenVibe.Events calls 127.0.0.1:4610 directly; anything that came@@@ -19,6 +19,8 @@ const { boot, check, done } = require('./helpers/app');assert.strictEqual(r.json.ready, true);const rel = await t.call('GET', '/release.json', { token: null });assert.strictEqual(rel.json.service, 'tips');++ assert.deepStrictEqual(require('openvibe-contracts').validate('registry.release-manifest@1', rel.json).errors, []);++ assert.strictEqual(rel.json.metrics_url, '/release-metrics');});await check('a service token for another audience, or without the capability, is refused', async () => {
@@ -41,7 +41,7 @@ from *interaction delivered*.
@@ -41,7 +41,7 @@ from *interaction delivered*.
(openvibe-sdk transactional outbox)
(openvibe-sdk transactional outbox)
- **OpenVibe.Network** — SSO for people, service tokens, JWKS, identity resolve (importer)
- **OpenVibe.Network** — SSO for people, service tokens, JWKS, identity resolve (importer)
- **OpenVibe.Live** — chat delivery through `/internal/tips/deliveries` (deployed in Live since `f11f809`; [docs/live-patch.diff](docs/live-patch.diff) is the original patch) until OpenVibe.Chat exposes paid messages; overlay consumer
- **OpenVibe.Live** — chat delivery through `/internal/tips/deliveries` (deployed in Live since `f11f809`; [docs/live-patch.diff](docs/live-patch.diff) is the original patch) until OpenVibe.Chat exposes paid messages; overlay consumer
-- **OpenVibe.Shared** v1.3.0 (app icon, SSR footer, noscript nav, release manifest, legal pages) and
+- **OpenVibe.Shared** v1.5.0 (app icon, SSR footer, noscript nav, release manifest, legal pages) and
the Network's `navbar.js`
the Network's `navbar.js`
## Run it
## Run it
@@ -121,9 +121,9 @@ token as a Bearer and act on their own things only. The API never reads cookies.
@@ -121,9 +121,9 @@ token as a Bearer and act on their own things only. The API never reads cookies.
| `GET /goals?creator=`, `GET /goals/:id` | `tips.goal.update` (private; full view) | public shape when the page is on (the creator's page settings); owner sees contributions |
| `GET /goals?creator=`, `GET /goals/:id` | `tips.goal.update` (private; full view) | public shape when the page is on (the creator's page settings); owner sees contributions |
| `GET /profiles/:creator/supporters` | `tips.profile.get` (while not public) | public when the creator shows the supporters page; the owner |
| `GET /profiles/:creator/supporters` | `tips.profile.get` (while not public) | public when the creator shows the supporters page; the owner |
| `GET /me/export`, `POST /me/erase` | — (people only) | the supporter's own tips: download; erase their data from them |
| `GET /me/export`, `POST /me/erase` | — (people only) | the supporter's own tips: download; erase their data from them |
-| `GET /moderation?creator=&state=held\|hidden\|visible\|all` | `tips.interaction.moderate` (proposed) | the creator and their moderators |
-| `POST /interactions/:id/hide`, `POST /interactions/:id/restore` `{reason?}` | `tips.interaction.moderate` (proposed) | the creator and their moderators |
-| `GET /moderation/log?creator=` | `tips.interaction.moderate` (proposed) | owner |
+| `GET /moderation?creator=&state=held\|hidden\|visible\|all` | `tips.interaction.moderate` | the creator and their moderators |
+| `POST /interactions/:id/hide`, `POST /interactions/:id/restore` `{reason?}` | `tips.interaction.moderate` | the creator and their moderators |
+| `GET /moderation/log?creator=` | `tips.interaction.moderate` | owner |
| `GET\|POST /moderators`, `POST /moderators/:subject/remove` | `tips.profile.update` | owner |
| `GET\|POST /moderators`, `POST /moderators/:subject/remove` | `tips.profile.update` | owner |
| `POST /goals`, `PATCH /goals/:id`, `POST /goals/:id/close` | `tips.goal.create` / `.update` / `.close` | owner |
| `POST /goals`, `PATCH /goals/:id`, `POST /goals/:id/close` | `tips.goal.create` / `.update` / `.close` | owner |
| `GET\|POST /overlay-tokens`, `POST /overlay-tokens/:id/revoke` | `tips.overlay.token.create` / `.revoke` | owner |
| `GET\|POST /overlay-tokens`, `POST /overlay-tokens/:id/revoke` | `tips.overlay.token.create` / `.revoke` | owner |
@@ -131,15 +131,16 @@ token as a Bearer and act on their own things only. The API never reads cookies.
@@ -131,15 +131,16 @@ token as a Bearer and act on their own things only. The API never reads cookies.
| `POST /simulate` | `tips.simulation.run` | owner |
| `POST /simulate` | `tips.simulation.run` | owner |
| `POST /internal/events` | Events webhook signature (`TIPS_EVENTS_SECRET`), loopback only | — |
| `POST /internal/events` | Events webhook signature (`TIPS_EVENTS_SECRET`), loopback only | — |
-`tips.interaction.moderate` is proposed in [docs/capabilities-proposal/](docs/capabilities-proposal/) and
-not in openvibe-contracts yet (grants are matched by string, so a Network grant of it works today).
+`tips.interaction.moderate` was proposed in [docs/capabilities-proposal/](docs/capabilities-proposal/)
+and is registered in openvibe-contracts v0.32.0.
The capabilities and the service manifest were released in openvibe-contracts v0.15.0 (3-segment ids:
The capabilities and the service manifest were released in openvibe-contracts v0.15.0 (3-segment ids:
the charter's `tips.simulate` is `tips.simulation.run`; `tips.interaction.record` is new, for EXTERNAL
the charter's `tips.simulate` is `tips.simulation.run`; `tips.interaction.record` is new, for EXTERNAL
tips); the drafts stay in [docs/capabilities-proposal/](docs/capabilities-proposal/) and
tips); the drafts stay in [docs/capabilities-proposal/](docs/capabilities-proposal/) and
[docs/service-manifest-proposal.json](docs/service-manifest-proposal.json). Grants are matched with
[docs/service-manifest-proposal.json](docs/service-manifest-proposal.json). Grants are matched with
-contracts' `capabilities.grants()` (exact id or a `.*` family). Tips pins openvibe-contracts v0.30.2,
-which also carries the payload schemas of the six `tips.*` events below; `test/contracts.test.js`
-validates every envelope and payload Tips produces against them.
+contracts' `capabilities.grants()` (exact id or a `.*` family). Tips pins openvibe-contracts v0.32.0,
+which also carries the payload schemas of the six `tips.*` events below (v0.30.2) and of
+`tips.interaction.moderated|erased` (v0.32.0); `test/contracts.test.js` validates every envelope and
+payload Tips produces against them.
## Privacy
## Privacy
@@ -203,8 +204,8 @@ toward its goal and the creator's totals.
@@ -203,8 +204,8 @@ toward its goal and the creator's totals.
Produced through the openvibe-sdk outbox (table `event_outbox`, source `tips`, same transaction as the
Produced through the openvibe-sdk outbox (table `event_outbox`, source `tips`, same transaction as the
change; relayed when `EVENTS_URL` and the client secret are set): `tips.interaction.ready`,
change; relayed when `EVENTS_URL` and the client secret are set): `tips.interaction.ready`,
`tips.interaction.failed`, `tips.interaction.cancelled`, `tips.goal.updated`, `tips.overlay.delivered`,
`tips.interaction.failed`, `tips.interaction.cancelled`, `tips.goal.updated`, `tips.overlay.delivered`,
-`tips.overlay.failed`, and two whose schemas are proposed in [docs/events-proposal/](docs/events-proposal/)
-(not yet in openvibe-contracts): `tips.interaction.erased` (a supporter's erasure; `{ interaction_id,
+`tips.overlay.failed`, and two whose schemas were proposed in [docs/events-proposal/](docs/events-proposal/)
+and are in openvibe-contracts since v0.32.0: `tips.interaction.erased` (a supporter's erasure; `{ interaction_id,
creator, erased_at, redacts }`) and `tips.interaction.moderated` (`{ interaction_id, creator, action:
creator, erased_at, redacts }`) and `tips.interaction.moderated` (`{ interaction_id, creator, action:
filtered|held|hidden|restored, by: filter|creator|moderator|service, moderation_state, cancelled_effects }`).
filtered|held|hidden|restored, by: filter|creator|moderator|service, moderation_state, cancelled_effects }`).
Simulations and Billing test money produce none. An anonymous supporter is never named in an event.
Simulations and Billing test money produce none. An anonymous supporter is never named in an event.
@@ -336,7 +337,7 @@ exist here (plan §12.12):
@@ -336,7 +337,7 @@ exist here (plan §12.12):
4. real persistence and end-to-end workflows — ✔ against stubs; not yet against the real Billing and
4. real persistence and end-to-end workflows — ✔ against stubs; not yet against the real Billing and
Events (Billing is in shadow and has sent no events; the production database is empty; the Live
Events (Billing is in shadow and has sent no events; the production database is empty; the Live
import has not been run);
import has not been run);
-5. capability and event registration against OpenVibe.Contracts — ✔ v0.15.0, payload schemas in v0.30.2 (pinned);
+5. capability and event registration against OpenVibe.Contracts — ✔ v0.15.0, payload schemas in v0.30.2 and v0.32.0 (pinned);
6. a migration/seed strategy ✔, a written threat review ✔ ([docs/threat-review.md](docs/threat-review.md),
6. a migration/seed strategy ✔, a written threat review ✔ ([docs/threat-review.md](docs/threat-review.md),
internal; an independent review is still due), and sitemap/robots ✔ (no feed);
internal; an independent review is still due), and sitemap/robots ✔ (no feed);
7. acceptance tests proving the advertised functionality — ✔ (table above).
7. acceptance tests proving the advertised functionality — ✔ (table above).
@@ -7,6 +7,6 @@
@@ -7,6 +7,6 @@
"adr": "ADR-012",
"adr": "ADR-012",
"authoritative": false,
"authoritative": false,
"deployment": "Deployed internally, not launched: openvibe-ovh /opt/openvibe.tips (release 435f32b), unit openvibe-tips, 127.0.0.1:4610 loopback only; https://openvibe.tips still serves the OpenVibe.Sites placeholder. Principal tips; Events subscription billing.transaction.* -> /internal/events; TIPS_CHAT_ADAPTER=none. Pins openvibe-contracts v0.15.0, openvibe-sdk v0.4.0, openvibe-shared v1.2.1.",
"deployment": "Deployed internally, not launched: openvibe-ovh /opt/openvibe.tips (release 435f32b), unit openvibe-tips, 127.0.0.1:4610 loopback only; https://openvibe.tips still serves the OpenVibe.Sites placeholder. Principal tips; Events subscription billing.transaction.* -> /internal/events; TIPS_CHAT_ADAPTER=none. Pins openvibe-contracts v0.15.0, openvibe-sdk v0.4.0, openvibe-shared v1.2.1.",
- "notes": "Runtime, API, SSR pages, overlays, Billing settlement consumer, events outbox, Live import and reconciliation are built and tested against stubs. Capabilities (17 tips.*) and the service manifest are registered in OpenVibe.Contracts v0.15.0; the six tips.* event payload schemas are in v0.30.2, which the repository pins since 2026-09-23 (production still runs v0.15.0). Production database is empty (0 profiles, interactions, goals, overlay tokens); the Live import (scripts/import-live.js) has never been run; 0 billing events have ever arrived because Billing runs in shadow. Live's /internal/tips/deliveries route is deployed (Live f11f809) but never called. The restore drill passed on the host 2026-09-23 05:23 UTC. Built since (not deployed yet): /metrics (loopback only) and the Billing/Events checks in /api/ready. Built since (not deployed yet): supporter privacy (anonymous, hidden amount, private message) across API, overlays, chat jobs, pages and events; the creator's public goal and supporters page settings; a supporter's export and erasure (tips.interaction.erased, schema proposed in docs/events-proposal/, not yet in openvibe-contracts). Moderation (not deployed yet): the creator's word filter (mask or hold for review) before anything is shown or read, hide/show by the creator, moderators (invitation links) or services, tips.interaction.moderated (schema and the tips.interaction.moderate capability proposed in docs/, not yet in openvibe-contracts). A written internal threat review (docs/threat-review.md) with its fixes and test/security.test.js. Not done: an independent security review; the openvibe.tips launch. Live stays the place tips happen until the Billing cutover (blocked on the owner re-pointing the PowerChat webhook) and the launch rule in README.md holds.",
+ "notes": "Runtime, API, SSR pages, overlays, Billing settlement consumer, events outbox, Live import and reconciliation are built and tested against stubs. Capabilities (17 tips.*) and the service manifest are registered in OpenVibe.Contracts v0.15.0; the six tips.* event payload schemas are in v0.30.2 and tips.interaction.moderated|erased in v0.32.0, which the repository pins since 2026-09-23 (production still runs v0.15.0). Production database is empty (0 profiles, interactions, goals, overlay tokens); the Live import (scripts/import-live.js) has never been run; 0 billing events have ever arrived because Billing runs in shadow. Live's /internal/tips/deliveries route is deployed (Live f11f809) but never called. The restore drill passed on the host 2026-09-23 05:23 UTC. Built since (not deployed yet): /metrics (loopback only) and the Billing/Events checks in /api/ready. Built since (not deployed yet): supporter privacy (anonymous, hidden amount, private message) across API, overlays, chat jobs, pages and events; the creator's public goal and supporters page settings; a supporter's export and erasure (tips.interaction.erased, schema in openvibe-contracts v0.32.0). Moderation (not deployed yet): the creator's word filter (mask or hold for review) before anything is shown or read, hide/show by the creator, moderators (invitation links) or services, tips.interaction.moderated (schema and the tips.interaction.moderate capability in openvibe-contracts v0.32.0). A written internal threat review (docs/threat-review.md) with its fixes and test/security.test.js. Not done: an independent security review; the openvibe.tips launch. Live stays the place tips happen until the Billing cutover (blocked on the owner re-pointing the PowerChat webhook) and the launch rule in README.md holds.",
"updated": "2026-09-23"
"updated": "2026-09-23"
}
}
@@ -14,9 +14,9 @@
@@ -14,9 +14,9 @@
"dotenv": "^16.4.7",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"express": "^4.21.2",
"jsonwebtoken": "^9.0.2",
"jsonwebtoken": "^9.0.2",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.30.2",
- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.4.0",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0"
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0"
},
},
"engines": {
"engines": {
"node": ">=22.0.0"
"node": ">=22.0.0"
@@ -983,9 +983,9 @@
@@ -983,9 +983,9 @@
}
}
},
},
"node_modules/openvibe-contracts": {
"node_modules/openvibe-contracts": {
- "version": "0.30.2",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.30.2",
- "integrity": "sha512-R01NgRY5V/GEYWkWUyxfl1MifSQb4dfJy11VS9RP+wsWedh2Crr9/IL7yGSn1PytwTKKL+tQl/r4ppGfH3TwLg==",
+ "version": "0.32.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",
+ "integrity": "sha512-ntN58SIG03YDxbhsiIzOM5GRH19jRMHZUYWI3xU2VwovExagF5DdwApLVfezsu84UTJ+V2Jfn+LWDc+Oz8ab+g==",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -999,18 +999,18 @@
@@ -999,18 +999,18 @@
}
}
},
},
"node_modules/openvibe-sdk": {
"node_modules/openvibe-sdk": {
- "version": "0.4.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.4.0",
- "integrity": "sha512-Hyz55YOMIs0xwCQ1NXsN0sUYhlbKXAX3uWY2fPCdEEL18FXUYVhK8wlG/5JMMypFHnjDiSd9VHzBNG12hWsmkQ==",
+ "version": "0.5.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
+ "integrity": "sha512-QVJkR5iazQX0ZwIrDp2A88ZeYYBSXEmSzhpgURPUSnzezIM3jz0HBhVKowyhkvg2aBO61XNf77eb4zJGkMjZiw==",
"license": "MIT",
"license": "MIT",
"engines": {
"engines": {
"node": ">=20"
"node": ">=20"
}
}
},
},
"node_modules/openvibe-shared": {
"node_modules/openvibe-shared": {
- "version": "1.3.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0",
- "integrity": "sha512-w8yl/5+9eend8I2O/DzAJnEAL2N3GTUY7c7pzK+MLJuLAGD8FmJJQ9gwbgyrq70Snjt3ebrg3TlpORhRAp2kgA==",
+ "version": "1.5.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0",
+ "integrity": "sha512-cehnscuYMkJwuCYGcI6wPSswbUn0bhTyGAaD5feNCoO//2e9gyC7cgjkEcIOtM8DQF3yTuUNyXnOwckIeM4fUg==",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
"jsonwebtoken": "^9.0.0"
"jsonwebtoken": "^9.0.0"
@@ -21,9 +21,9 @@
@@ -21,9 +21,9 @@
"dotenv": "^16.4.7",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"express": "^4.21.2",
"jsonwebtoken": "^9.0.2",
"jsonwebtoken": "^9.0.2",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.30.2",
- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.4.0",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0"
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0"
},
},
"engines": {
"engines": {
"node": ">=22.0.0"
"node": ">=22.0.0"
@@ -61,7 +61,7 @@ const CAP = {
@@ -61,7 +61,7 @@ const CAP = {
configGet: 'tips.overlay.config.get',
configGet: 'tips.overlay.config.get',
configUpdate: 'tips.overlay.config.update',
configUpdate: 'tips.overlay.config.update',
simulate: 'tips.simulation.run',
simulate: 'tips.simulation.run',
- moderate: 'tips.interaction.moderate', // proposed (docs/capabilities-proposal/), not yet in openvibe-contracts
+ moderate: 'tips.interaction.moderate', // in openvibe-contracts since v0.32.0 (was docs/capabilities-proposal/)
};
};
function v1Router({ domain, apiAuth }) {
function v1Router({ domain, apiAuth }) {
@@ -82,7 +82,8 @@ function createApp(opts = {}) {
@@ -82,7 +82,8 @@ function createApp(opts = {}) {
// Events are optional and degrade it (see observability.js).
// Events are optional and degrade it (see observability.js).
const readiness = createTipsReadiness({ db, keys, config, outbox, release: release.release, fetchImpl });
const readiness = createTipsReadiness({ db, keys, config, outbox, release: release.release, fetchImpl });
app.get('/api/ready', readiness.handler);
app.get('/api/ready', readiness.handler);
- app.get('/release.json', release.handler);
+ // GET /release.json (ADR-016) and POST /release-metrics: open tabs' update reports into /metrics.
+ release.mount(app, { registry: metrics.registry });
const consumer = consumerRouter({ domain, config, log });
const consumer = consumerRouter({ domain, config, log });
// Service-to-service only: OpenVibe.Events calls 127.0.0.1:4610 directly; anything that came
// Service-to-service only: OpenVibe.Events calls 127.0.0.1:4610 directly; anything that came
@@ -19,6 +19,8 @@ const { boot, check, done } = require('./helpers/app');
@@ -19,6 +19,8 @@ const { boot, check, done } = require('./helpers/app');
assert.strictEqual(r.json.ready, true);
assert.strictEqual(r.json.ready, true);
const rel = await t.call('GET', '/release.json', { token: null });
const rel = await t.call('GET', '/release.json', { token: null });
assert.strictEqual(rel.json.service, 'tips');
assert.strictEqual(rel.json.service, 'tips');
+ assert.deepStrictEqual(require('openvibe-contracts').validate('registry.release-manifest@1', rel.json).errors, []);
+ assert.strictEqual(rel.json.metrics_url, '/release-metrics');
});
});
await check('a service token for another audience, or without the capability, is refused', async () => {
await check('a service token for another audience, or without the capability, is refused', async () => {