openvibers/openvibe.live Commits

openvibe-contracts v0.32.0: live.lineage.resolve is written inline in the guard, so the CI contract check enforces it (6 capabilities now); the lineage resolver test requires lineage.resolution@1 and validates every answer; live.stream.started/ended and live.release.deployed payloads are validated against their new contracts (and the started event's channel link is /@<username>). Stale comments saying these were not in the registry are gone

OP OpenVibers committed 23 Sept 2026 · ca7cc87672 · 8 files, +18 −23


Changes

ddiff --git a/package-lock.json b/package-lock.json
iindex a2ab1dc7..7c39043d 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -25,7 +25,7 @@
"multer": "^1.4.5-lts.1",
"node-media-server": "^2.7.1",
"onnxruntime-node": "^1.20.0",
-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.30.2",
++ "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.4.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.4.0",
"sanitize-html": "^2.17.7",
@@@ -2814,9 +2814,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",
ddiff --git a/package.json b/package.json
iindex 82eb3ade..12ca9b3a 100644
---- a/package.json
++++ b/package.json
@@@ -43,7 +43,7 @@
"multer": "^1.4.5-lts.1",
"node-media-server": "^2.7.1",
"onnxruntime-node": "^1.20.0",
-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.30.2",
++ "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.4.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.4.0",
"sanitize-html": "^2.17.7",
ddiff --git a/server/events/release-events.js b/server/events/release-events.js
iindex e05a188b..b4506e0a 100644
---- a/server/events/release-events.js
++++ b/server/events/release-events.js
@@@ -15,8 +15,8 @@
* first, at most 40), deployed_at, notes_url }
*
* Visibility is `internal`: the commit subjects are public in chat and on /updates already, but
-- * the consumer (the chat notice) decides what is shown. No contract in openvibe-contracts v0.30.1
-- * defines this type yet; it is listed as a contract addition for the `live.*` namespace Live owns.
++ * the consumer (the chat notice) decides what is shown. Contract: live.release.deployed@1
++ * (openvibe-contracts 0.32.0).
*/
const EVENT_TYPE = 'live.release.deployed';
const NOTES_URL = 'https://openvibe.live/updates';
ddiff --git a/server/lineage/routes.js b/server/lineage/routes.js
iindex ed9ea9ab..48594a91 100644
---- a/server/lineage/routes.js
++++ b/server/lineage/routes.js
@@@ -10,23 +10,14 @@
*
* Every well-formed request is answered 200 with a lineage.resolution@1, resolved or unresolved
* (a conflict, a display name alone, Media down); a malformed one is 400 lineage.invalid_request.
-- *
-- * The capability id is passed as a constant, not a string literal inside the guard call:
-- * live.lineage.resolve is registered in openvibe-contracts 0.32.0, Live still pins 0.30.2, and
-- * `openvibe-contracts-check` in CI fails on a literal capability id the pinned registry does not
-- * define (it scans comments too). guard() checks the grant itself while the id is unknown to the
-- * registry and switches to capabilities.check() once the pin moves. When Live pins 0.32.0+, inline
-- * the id so the CI check enforces it.
*/
const express = require('express');
const { http } = require('openvibe-contracts');
const { guard } = require('../net/service-guard');
const resolver = require('./resolver');
--const CAPABILITY = 'live.lineage.resolve';
--
const router = express.Router();
--router.use(guard(CAPABILITY));
++router.use(guard('live.lineage.resolve'));
async function answer(req, res, raw, flat) {
const { input, error } = resolver.normalizeRequest(raw, { flat });
ddiff --git a/server/net/service-guard.js b/server/net/service-guard.js
iindex e4b13d40..cb2b4307 100644
---- a/server/net/service-guard.js
++++ b/server/net/service-guard.js
@@@ -7,11 +7,10 @@
* today the ones OpenVibe.Chat calls (/internal/chat-context/*, /internal/chat-effects/*). The
* token is verified with the same Network key Live verifies user JWTs with.
*
-- * Chat's capabilities (live.chat_context.read, live.chat_effects.write, live.chat_mirror.write)
-- * are proposals not yet in the openvibe-contracts registry, whose requireCapability() refuses
-- * unknown ids; until they are registered the grant is checked with capabilities.grants() and the
-- * guard switches to capabilities.check() by itself once the registry knows the id. Like
-- * X-Internal-Key routes, these are loopback-only: anything that came through nginx is refused.
++ * The capability ids are registered in openvibe-contracts (checked with capabilities.check()); an
++ * id the pinned registry does not know yet is checked as a plain grant with capabilities.grants()
++ * until the pin moves. Like X-Internal-Key routes, these are loopback-only: anything that came
++ * through nginx is refused.
*/
const { serviceAuth, capabilities, http } = require('openvibe-contracts');
const { getNetworkPublicKey, getNetworkIssuer } = require('../auth/auth');
ddiff --git a/test/lineage-resolver.test.js b/test/lineage-resolver.test.js
iindex 6f70d1d8..a7133613 100644
---- a/test/lineage-resolver.test.js
++++ b/test/lineage-resolver.test.js
@@@ -72,9 +72,10 @@ const mediaServer = http.createServer((req, res) => {
send(404, { error: 'no route' });
});
--// Every contract answer is checked against lineage.resolution@1 when the installed contracts know it.
++// Every contract answer is checked against lineage.resolution@1 (openvibe-contracts 0.32.0+, which Live pins).
const RESOLUTION = 'lineage.resolution@1';
const contractKnown = (() => { try { contracts.resolve(RESOLUTION); return true; } catch { return false; } })();
++assert.ok(contractKnown, `the pinned openvibe-contracts defines ${RESOLUTION}`);
let validated = 0;
function checkContract(out, label) {
if (!contractKnown) return out;
ddiff --git a/test/release-events.test.js b/test/release-events.test.js
iindex 64340e27..7f556847 100644
---- a/test/release-events.test.js
++++ b/test/release-events.test.js
@@@ -53,6 +53,7 @@ const stub = http.createServer((req, res) => {
const sample = releaseEvents.envelopeFor({ head, previous: null, commits: [{ hash: head, short: head.slice(0, 7), subject: 'x', date: '2026-09-23T00:00:00Z' }] });
const full = { ...sample, event_id: 'evt_01JAB2C3D4E5F6G7H8J9K0MNPQ', version: 1, source: 'live', timestamp: new Date().toISOString() };
contracts.assertValid('events.event-envelope@1', full);
++ contracts.assertValid('live.release.deployed@1', full.payload);
assert.strictEqual(sample.payload.release, head.slice(0, 7));
assert.throws(() => releaseEvents.envelopeFor({ head: 'nope' }), /full commit sha/);
ddiff --git a/test/stream-events.test.js b/test/stream-events.test.js
iindex 41f499ba..561a003f 100644
---- a/test/stream-events.test.js
++++ b/test/stream-events.test.js
@@@ -75,6 +75,8 @@ const stub = http.createServer((req, res) => {
assert.strictEqual(ev.payload.channel.username, 'streamer');
assert.match(ev.payload.started_at, /^\d{4}-\d\d-\d\dT/);
assert.match(ev.event_id, /^evt_[0-9A-HJKMNP-TV-Z]{26}$/);
++ assert.strictEqual(ev.payload.channel.url, 'https://openvibe.live/@streamer', 'the channel link is the channel page');
++ require('openvibe-contracts').assertValid('live.stream.started@1', ev.payload);
// Ending a stream that is already ended emits nothing; ending a live one emits ended.
db.endStream(id);
@@@ -84,6 +86,7 @@ const stub = http.createServer((req, res) => {
assert.strictEqual(published[1].event_type, 'live.stream.ended');
assert.strictEqual(published[1].subject.revision, 2);
assert.ok('duration_seconds' in published[1].payload);
++ require('openvibe-contracts').assertValid('live.stream.ended@1', published[1].payload);
// A rolled-back go-live leaves no event.
assert.throws(() => d.transaction(() => { db.createStream({ user_id: 501, title: 'rolled back' }); throw new Error('abort'); })());