openvibers/openvibe.network Commits

openvibe-contracts v0.26.0 (developer-project claims, sandbox refusal); grants and clients for deals, coupons and host; tests follow services that left placeholder

OP OpenVibers committed 23 Sept 2026 · 2030e362ec · 7 files, +25 −11


Changes

ddiff --git a/package-lock.json b/package-lock.json
iindex ec322935..4e76797e 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -20,7 +20,7 @@
"helmet": "^8.1.0",
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.13.0",
++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.26.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0",
"uuid": "^11.1.0",
"web-push": "^3.6.7"
@@@ -1464,9 +1464,9 @@
}
},
"node_modules/openvibe-contracts": {
-- "version": "0.13.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.13.0",
-- "integrity": "sha512-MeIGJYHQsbr6ISeEErduUw/PD52X5IBclH2ht/G/t4lSX5X4o2AwcZYLEzOZrcbaXQyplgC7r86RCGFvi1ifUA==",
++ "version": "0.26.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.26.0",
++ "integrity": "sha512-yigHjNzLIyK578V0Weqv2zL6z8PO8ce3HRXPa/61n1XK5Y7pTi28qHMMiO6stboQjTziN1+8wMeByoCF1sGCnw==",
"license": "AGPL-3.0-only",
"dependencies": {
"ajv": "^8.17.1",
ddiff --git a/package.json b/package.json
iindex e32e107b..36403679 100644
---- a/package.json
++++ b/package.json
@@@ -23,7 +23,7 @@
"helmet": "^8.1.0",
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.13.0",
++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.26.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0",
"uuid": "^11.1.0",
"web-push": "^3.6.7"
ddiff --git a/server/db/database.js b/server/db/database.js
iindex f02df929..7818ac2f 100644
---- a/server/db/database.js
++++ b/server/db/database.js
@@@ -591,6 +591,9 @@ function initDb(dbPath) {
{ client_id: 'reviews', name: 'OpenVibe.Reviews', redirect_uris: ['https://openvibe.reviews/auth/callback'] },
{ client_id: 'news', name: 'OpenVibe.News', redirect_uris: ['https://openvibe.news/auth/callback'] },
{ client_id: 'trade', name: 'OpenVibe.Trade', redirect_uris: ['https://openvibe.trade/auth/callback'] },
++ { client_id: 'deals', name: 'OpenVibe.Deals', redirect_uris: ['https://openvibe.deals/auth/callback'] },
++ { client_id: 'coupons', name: 'OpenVibe.Coupons', redirect_uris: ['https://openvibe.coupons/auth/callback'] },
++ { client_id: 'host', name: 'OpenVibe.Host', redirect_uris: ['https://openvibe.host/auth/callback'] },
// Billing's staff console (Wave 8): Network admins listed in BILLING_STAFF_SUBJECTS.
{ client_id: 'billing', name: 'OpenVibe.Billing', redirect_uris: ['https://billing.openvibe.network/auth/callback'] },
];
ddiff --git a/server/identity/principals.js b/server/identity/principals.js
iindex 3026036d..687438f9 100644
---- a/server/identity/principals.js
++++ b/server/identity/principals.js
@@@ -83,6 +83,16 @@ const DEFAULT_GRANTS = [
['trade', 'events.event.publish', 'openvibe.events', []],
['trade', 'sources.item.read', 'openvibe.sources', []],
['trade', 'sources.source.read', 'openvibe.sources', []],
++ // Wave 18: Deals and Coupons; Wave 21 Stage B: the Host API publishes deploy/domain events.
++ ['deals', 'identity.subject.resolve', SELF_AUDIENCE, []],
++ ['deals', 'events.event.publish', 'openvibe.events', []],
++ ['deals', 'events.subscription.manage', 'openvibe.events', []],
++ ['deals', 'sources.item.read', 'openvibe.sources', []],
++ ['deals', 'community.comment.write', 'openvibe.community', []],
++ ['deals', 'community.comment.moderate', 'openvibe.community', []],
++ ['coupons', 'events.event.publish', 'openvibe.events', []],
++ ['coupons', 'sources.item.read', 'openvibe.sources', []],
++ ['host', 'events.event.publish', 'openvibe.events', []],
// Wave 3: producers publish to OpenVibe.Events (their own source only, enforced by Events).
...['live', 'media', 'network', 'community', 'billing', 'chat', 'tools', 'games', 'search', 'sources', 'tips'].map(c => [c, 'events.event.publish', 'openvibe.events', []]),
// Wave 14: Search subscribes to <owner>.index_document.* deliveries.
ddiff --git a/test/developer-projects.test.js b/test/developer-projects.test.js
iindex 95cd01e1..9462900a 100644
---- a/test/developer-projects.test.js
++++ b/test/developer-projects.test.js
@@@ -61,7 +61,8 @@ const secretsSeen = [];
const token = (form) => fetch(`${base}/oauth/token`, { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams(form) })
.then(async r => ({ status: r.status, cache: r.headers.get('cache-control'), body: await r.json() }));
const cc = (appId, secret, audience = 'openvibe.media', extra = {}) => token({ grant_type: 'client_credentials', client_id: appId, client_secret: secret, audience, ...extra });
-- const verify = (t, audience = 'openvibe.media') => serviceAuth.verifyServiceToken(t, { publicKey: keys.publicKey, issuer: ISSUER, audience });
++ // Inspects token contents, so it opts in to sandbox tokens (contracts v0.26.0 refuses them by default).
++ const verify = (t, audience = 'openvibe.media') => serviceAuth.verifyServiceToken(t, { publicKey: keys.publicKey, issuer: ISSUER, audience, acceptSandbox: true });
// ── Authentication: Bearer user tokens only; never X-Internal-Key ──
let r = await api(null, 'GET', '', null, { 'x-internal-key': 'legacy-key' });
ddiff --git a/test/registry-ecosystem.test.js b/test/registry-ecosystem.test.js
iindex 2de5b7c0..d1c6dcdd 100644
---- a/test/registry-ecosystem.test.js
++++ b/test/registry-ecosystem.test.js
@@@ -30,10 +30,10 @@ const { createEcosystemRegistry } = require('../server/registry/ecosystem');
assert.strictEqual(byId.network.runtime.status, 'up');
assert.ok(byId.network.runtime.checked_at, 'every health value says when it was checked');
assert.strictEqual(byId.live.runtime.status, 'down', 'unreachable is reported, not hidden');
-- assert.strictEqual(byId.wiki.runtime.status, 'not-running', 'placeholders are never shown as running');
++ assert.strictEqual(byId.codes.runtime.status, 'not-running', 'placeholders are never shown as running');
assert.ok(r.body.services.every(s => contracts.validate('registry.service-manifest@1', Object.fromEntries(Object.entries(s).filter(([k]) => k !== 'runtime'))).valid));
r = await get('/api/v1/registry/services?status=placeholder');
-- assert.ok(r.body.services.length >= 15 && r.body.services.every(s => s.status === 'placeholder'));
++ assert.ok(r.body.services.length >= 2 && r.body.services.every(s => s.status === 'placeholder'));
r = await get('/api/v1/registry/services/network');
assert.ok(r.body.capability_details.some(c => c.id === 'network.coins.credit'));
ddiff --git a/test/status.test.js b/test/status.test.js
iindex fc5bab6b..d7f5f1e0 100644
---- a/test/status.test.js
++++ b/test/status.test.js
@@@ -72,8 +72,8 @@ const rel = (release) => [200, { service: 'x', release, released_at: '2026-09-22
assert.ok(by.tools.release_error);
assert.strictEqual(by.events.basis, 'ready-legacy');
assert.strictEqual(by.live.status, 'down', 'unreachable is down');
-- assert.strictEqual(by.wiki.status, 'not-running');
-- assert.strictEqual(by.wiki.label, 'not running (placeholder)');
++ assert.strictEqual(by.codes.status, 'not-running');
++ assert.strictEqual(by.codes.label, 'not running (placeholder)');
assert.strictEqual(by.contracts.status, 'not-running');
assert.strictEqual(by.contracts.label, 'not running (no runtime)');
for (const s of r.body.services) assert.ok(s.checked_at, `${s.id} says when it was checked`);
@@@ -106,7 +106,7 @@ const rel = (release) => [200, { service: 'x', release, released_at: '2026-09-22
assert.ok(html.includes('<meta name="robots" content="noindex, nofollow">'));
assert.ok(html.includes('id="svc-media"') && /id="svc-media"[\s\S]*?Degraded/.test(html));
assert.ok(/id="svc-community"[\s\S]*?Down[\s\S]*?SQLITE_CANTOPEN/.test(html));
-- assert.ok(/id="svc-wiki"[\s\S]*?not running \(placeholder\)/.test(html));
++ assert.ok(/id="svc-codes"[\s\S]*?not running \(placeholder\)/.test(html));
assert.ok(/id="svc-tools"[\s\S]*?liveness only/.test(html));
assert.ok(html.includes('<noscript>'), 'navigation without JavaScript');
assert.ok(html.includes('SLO categories (proposals)'));