openvibers/openvibe.network Commits
openvibe-contracts v0.34.1 (Tools platform S7): sandbox apps hold tools.tool.read and tools.tool.run by default (the run API is live); the self-service catalog lists public capabilities only (partner ones like tools.net.probe are staff-granted); the publishing library joins the registry's libraries; realtime is retired (ADR-005) in the registry tests
Changes
@@@ -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.33.1",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.1","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.1","uuid": "^11.1.0",@@@ -1465,9 +1465,9 @@}},"node_modules/openvibe-contracts": {-- "version": "0.33.1",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.1",-- "integrity": "sha512-0S6SubDzdBmmIMbb4x1gWjWieqCl9W6nuvKrv/voUBpEvJChMGKlRmWnhLQV0KU7IZGxZcDahoYe9vI03pmf+A==",++ "version": "0.34.1",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.1",++ "integrity": "sha512-Lzb5nrT24AbUpwMmRsf3vEDcQtLlIgC0XJUmknCR8yqGUe5jfyyMFXdfVvEa8t7elAm53mw5hdz7YC6QMz/c0w==","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -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.33.1",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.1","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.1","uuid": "^11.1.0",@@@ -37,7 +37,8 @@ function audienceOf(id) {/** Capabilities an app could ever be granted (the catalog view Codes shows). */function grantableCatalog() {-- return capabilities.manifests.filter(c => isGrantable(c.id)).map(c => ({++ // What an app may ask for itself: public capabilities. Partner ones (e.g. tools.net.probe) are granted by staff only.++ return capabilities.manifests.filter(c => isGrantable(c.id) && c.visibility === 'public').map(c => ({id: c.id, owner: c.owner, audience: `openvibe.${c.owner}`, visibility: c.visibility, description: c.description || '',resourceConstraints: c.resourceConstraints, quotaClass: c.quotaClass,}));@@@ -59,7 +60,7 @@ const DEFAULT_SANDBOX_AUDIENCES = Object.freeze(['openvibe.media', 'openvibe.eveconst DEFAULT_SANDBOX_ALLOWANCE = Object.freeze(['media.object.upload', 'media.object.read','events.app.publish', 'events.app.read', 'events.app.subscribe',-- 'tools.job.create', 'tools.job.read', 'tools.job.cancel',++ 'tools.job.create', 'tools.job.read', 'tools.job.cancel', 'tools.tool.read', 'tools.tool.run',]);/** Public + grantable only (the rule for every allowance that is not set by staff by hand). */@@@ -88,6 +88,7 @@ const EXPOSURE = {sdk: library('openvibe-sdk', 'OpenVibe.SDK'),shared: library('openvibe-shared', 'OpenVibe.Shared', 'also served at /shared/* by each site'),contracts: library('openvibe-contracts', 'OpenVibe.Contracts', 'schemas are also served by this registry at their $id URLs'),++ publishing: library('openvibe-publishing', 'OpenVibe.Publishing', 'the shared publishing runtime used by Wiki, Blog, News, Reviews, Deals, Coupons and Trade'),examples: { state: 'repository', public_site: null, note: 'example apps with CI in OpenVibers/OpenVibe.Examples; not published as a package' },realtime: { state: 'placeholder', public_site: null },@@@ -69,7 +69,7 @@ async function withoutCatalog(hidden, fn) {assert.deepStrictEqual([...s.sandboxAudiences].sort(), ['openvibe.events', 'openvibe.media', 'openvibe.tools']);assert.ok(!s.sandboxAudiences.has('openvibe.network'), 'Network itself never defaults to accepting sandbox tokens');assert.deepStrictEqual(s.sandboxAllowance, ['events.app.publish', 'events.app.read', 'events.app.subscribe',-- 'media.object.read', 'media.object.upload', 'tools.job.cancel', 'tools.job.create', 'tools.job.read'], 'the default sandbox allowance');++ 'media.object.read', 'media.object.upload', 'tools.job.cancel', 'tools.job.create', 'tools.job.read', 'tools.tool.read', 'tools.tool.run'], 'the default sandbox allowance');assert.deepStrictEqual(policy.DEFAULT_SANDBOX_ALLOWANCE.filter(id => id.startsWith('events.')), ['events.app.publish', 'events.app.read', 'events.app.subscribe']);s = policy.settings({ developer: { sandboxAudiences: '', sandboxAllowance: '' } });assert.strictEqual(s.sandboxAudiences.size, 0, 'set-but-empty means none');@@@ -33,9 +33,10 @@ const { createEcosystemRegistry } = require('../server/registry/ecosystem');assert.strictEqual(byId.realtime.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]) => !['runtime', 'exposure', 'observed'].includes(k)))).valid));r = await get('/api/v1/registry/services?status=placeholder');-- // openvibe-contracts 0.30: realtime is the only placeholder manifest left (AI, SDK, Shared, Examples are alpha).-- assert.ok(r.body.services.length >= 1 && r.body.services.every(s => s.status === 'placeholder'));-- assert.ok(r.body.services.some(s => s.id === 'realtime'));++ assert.ok(r.body.services.every(s => s.status === 'placeholder'), 'the status filter only returns that status');++ // openvibe-contracts 0.34: realtime is retired (ADR-005 folded it into Events).++ r = await get('/api/v1/registry/services?status=retired');++ assert.ok(r.body.services.some(s => s.id === 'realtime') && r.body.services.every(s => s.status === 'retired'));r = await get('/api/v1/registry/services/network');assert.ok(r.body.capability_details.some(c => c.id === 'network.coins.credit'));@@@ -19,7 +19,7 @@ const PINNED = {billing: 'live', codes: 'live', blog: 'live', wiki: 'live', sites: 'live',news: 'internal', reviews: 'internal', deals: 'internal', coupons: 'internal', trade: 'internal', host: 'internal',tips: 'internal', vip: 'internal', openre: 'internal', search: 'live', sources: 'internal', chat: 'internal', ai: 'internal',-- sdk: 'library', shared: 'library', contracts: 'library',++ sdk: 'library', shared: 'library', contracts: 'library', publishing: 'library',examples: 'repository',realtime: 'placeholder',};@@@ -101,7 +101,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], chassert.strictEqual(sBy.live.label, 'up');assert.strictEqual(sBy.live.origin, 'https://openvibe.live');assert.strictEqual(sBy.shared.label, `not running (library, released v${require('openvibe-shared/package.json').version})`);-- assert.deepStrictEqual(st.exposure_summary, { live: 13, internal: 12, library: 3, repository: 1, placeholder: 1 });++ assert.deepStrictEqual(st.exposure_summary, { live: 13, internal: 12, library: 4, repository: 1, placeholder: 1 });// The page: no row for a service whose domain serves a placeholder shows a bare "Up".const html = await fetch(base + '/status').then(x => x.text());@@@ -102,7 +102,7 @@ const rel = (contractsVersion, packages) => [200, { service: 'x', release: 'abcdr = await get('/api/v1/registry/releases');const rel2 = Object.fromEntries(r.body.services.map(s => [s.id, s]));assert.ok(r.body.checked_at);-- assert.deepStrictEqual(r.body.libraries.map(l => l.id).sort(), ['contracts', 'sdk', 'shared']);++ assert.deepStrictEqual(r.body.libraries.map(l => l.id).sort(), ['contracts', 'publishing', 'sdk', 'shared']);assert.ok(!rel2.sdk && !rel2.contracts, 'libraries are not services');assert.strictEqual(rel2.live.release, 'abcdef123456');assert.strictEqual(rel2.live.contracts_version, '0.28.0');
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
"helmet": "^8.1.0",
"helmet": "^8.1.0",
"jsonwebtoken": "^9.0.0",
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
"multer": "^1.4.5-lts.1",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.1",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.1",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.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.1",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1",
"uuid": "^11.1.0",
"uuid": "^11.1.0",
@@ -1465,9 +1465,9 @@
@@ -1465,9 +1465,9 @@
}
}
},
},
"node_modules/openvibe-contracts": {
"node_modules/openvibe-contracts": {
- "version": "0.33.1",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.1",
- "integrity": "sha512-0S6SubDzdBmmIMbb4x1gWjWieqCl9W6nuvKrv/voUBpEvJChMGKlRmWnhLQV0KU7IZGxZcDahoYe9vI03pmf+A==",
+ "version": "0.34.1",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.1",
+ "integrity": "sha512-Lzb5nrT24AbUpwMmRsf3vEDcQtLlIgC0XJUmknCR8yqGUe5jfyyMFXdfVvEa8t7elAm53mw5hdz7YC6QMz/c0w==",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -23,7 +23,7 @@
@@ -23,7 +23,7 @@
"helmet": "^8.1.0",
"helmet": "^8.1.0",
"jsonwebtoken": "^9.0.0",
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
"multer": "^1.4.5-lts.1",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.1",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.34.1",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.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.1",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1",
"uuid": "^11.1.0",
"uuid": "^11.1.0",
@@ -37,7 +37,8 @@ function audienceOf(id) {
@@ -37,7 +37,8 @@ function audienceOf(id) {
/** Capabilities an app could ever be granted (the catalog view Codes shows). */
/** Capabilities an app could ever be granted (the catalog view Codes shows). */
function grantableCatalog() {
function grantableCatalog() {
- return capabilities.manifests.filter(c => isGrantable(c.id)).map(c => ({
+ // What an app may ask for itself: public capabilities. Partner ones (e.g. tools.net.probe) are granted by staff only.
+ return capabilities.manifests.filter(c => isGrantable(c.id) && c.visibility === 'public').map(c => ({
id: c.id, owner: c.owner, audience: `openvibe.${c.owner}`, visibility: c.visibility, description: c.description || '',
id: c.id, owner: c.owner, audience: `openvibe.${c.owner}`, visibility: c.visibility, description: c.description || '',
resourceConstraints: c.resourceConstraints, quotaClass: c.quotaClass,
resourceConstraints: c.resourceConstraints, quotaClass: c.quotaClass,
}));
}));
@@ -59,7 +60,7 @@ const DEFAULT_SANDBOX_AUDIENCES = Object.freeze(['openvibe.media', 'openvibe.eve
@@ -59,7 +60,7 @@ const DEFAULT_SANDBOX_AUDIENCES = Object.freeze(['openvibe.media', 'openvibe.eve
const DEFAULT_SANDBOX_ALLOWANCE = Object.freeze([
const DEFAULT_SANDBOX_ALLOWANCE = Object.freeze([
'media.object.upload', 'media.object.read',
'media.object.upload', 'media.object.read',
'events.app.publish', 'events.app.read', 'events.app.subscribe',
'events.app.publish', 'events.app.read', 'events.app.subscribe',
- 'tools.job.create', 'tools.job.read', 'tools.job.cancel',
+ 'tools.job.create', 'tools.job.read', 'tools.job.cancel', 'tools.tool.read', 'tools.tool.run',
]);
]);
/** Public + grantable only (the rule for every allowance that is not set by staff by hand). */
/** Public + grantable only (the rule for every allowance that is not set by staff by hand). */
@@ -88,6 +88,7 @@ const EXPOSURE = {
@@ -88,6 +88,7 @@ const EXPOSURE = {
sdk: library('openvibe-sdk', 'OpenVibe.SDK'),
sdk: library('openvibe-sdk', 'OpenVibe.SDK'),
shared: library('openvibe-shared', 'OpenVibe.Shared', 'also served at /shared/* by each site'),
shared: library('openvibe-shared', 'OpenVibe.Shared', 'also served at /shared/* by each site'),
contracts: library('openvibe-contracts', 'OpenVibe.Contracts', 'schemas are also served by this registry at their $id URLs'),
contracts: library('openvibe-contracts', 'OpenVibe.Contracts', 'schemas are also served by this registry at their $id URLs'),
+ publishing: library('openvibe-publishing', 'OpenVibe.Publishing', 'the shared publishing runtime used by Wiki, Blog, News, Reviews, Deals, Coupons and Trade'),
examples: { state: 'repository', public_site: null, note: 'example apps with CI in OpenVibers/OpenVibe.Examples; not published as a package' },
examples: { state: 'repository', public_site: null, note: 'example apps with CI in OpenVibers/OpenVibe.Examples; not published as a package' },
realtime: { state: 'placeholder', public_site: null },
realtime: { state: 'placeholder', public_site: null },
@@ -69,7 +69,7 @@ async function withoutCatalog(hidden, fn) {
@@ -69,7 +69,7 @@ async function withoutCatalog(hidden, fn) {
assert.deepStrictEqual([...s.sandboxAudiences].sort(), ['openvibe.events', 'openvibe.media', 'openvibe.tools']);
assert.deepStrictEqual([...s.sandboxAudiences].sort(), ['openvibe.events', 'openvibe.media', 'openvibe.tools']);
assert.ok(!s.sandboxAudiences.has('openvibe.network'), 'Network itself never defaults to accepting sandbox tokens');
assert.ok(!s.sandboxAudiences.has('openvibe.network'), 'Network itself never defaults to accepting sandbox tokens');
assert.deepStrictEqual(s.sandboxAllowance, ['events.app.publish', 'events.app.read', 'events.app.subscribe',
assert.deepStrictEqual(s.sandboxAllowance, ['events.app.publish', 'events.app.read', 'events.app.subscribe',
- 'media.object.read', 'media.object.upload', 'tools.job.cancel', 'tools.job.create', 'tools.job.read'], 'the default sandbox allowance');
+ 'media.object.read', 'media.object.upload', 'tools.job.cancel', 'tools.job.create', 'tools.job.read', 'tools.tool.read', 'tools.tool.run'], 'the default sandbox allowance');
assert.deepStrictEqual(policy.DEFAULT_SANDBOX_ALLOWANCE.filter(id => id.startsWith('events.')), ['events.app.publish', 'events.app.read', 'events.app.subscribe']);
assert.deepStrictEqual(policy.DEFAULT_SANDBOX_ALLOWANCE.filter(id => id.startsWith('events.')), ['events.app.publish', 'events.app.read', 'events.app.subscribe']);
s = policy.settings({ developer: { sandboxAudiences: '', sandboxAllowance: '' } });
s = policy.settings({ developer: { sandboxAudiences: '', sandboxAllowance: '' } });
assert.strictEqual(s.sandboxAudiences.size, 0, 'set-but-empty means none');
assert.strictEqual(s.sandboxAudiences.size, 0, 'set-but-empty means none');
@@ -33,9 +33,10 @@ const { createEcosystemRegistry } = require('../server/registry/ecosystem');
@@ -33,9 +33,10 @@ const { createEcosystemRegistry } = require('../server/registry/ecosystem');
assert.strictEqual(byId.realtime.runtime.status, 'not-running', 'placeholders are never shown as running');
assert.strictEqual(byId.realtime.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]) => !['runtime', 'exposure', 'observed'].includes(k)))).valid));
assert.ok(r.body.services.every(s => contracts.validate('registry.service-manifest@1', Object.fromEntries(Object.entries(s).filter(([k]) => !['runtime', 'exposure', 'observed'].includes(k)))).valid));
r = await get('/api/v1/registry/services?status=placeholder');
r = await get('/api/v1/registry/services?status=placeholder');
- // openvibe-contracts 0.30: realtime is the only placeholder manifest left (AI, SDK, Shared, Examples are alpha).
- assert.ok(r.body.services.length >= 1 && r.body.services.every(s => s.status === 'placeholder'));
- assert.ok(r.body.services.some(s => s.id === 'realtime'));
+ assert.ok(r.body.services.every(s => s.status === 'placeholder'), 'the status filter only returns that status');
+ // openvibe-contracts 0.34: realtime is retired (ADR-005 folded it into Events).
+ r = await get('/api/v1/registry/services?status=retired');
+ assert.ok(r.body.services.some(s => s.id === 'realtime') && r.body.services.every(s => s.status === 'retired'));
r = await get('/api/v1/registry/services/network');
r = await get('/api/v1/registry/services/network');
assert.ok(r.body.capability_details.some(c => c.id === 'network.coins.credit'));
assert.ok(r.body.capability_details.some(c => c.id === 'network.coins.credit'));
@@ -19,7 +19,7 @@ const PINNED = {
@@ -19,7 +19,7 @@ const PINNED = {
billing: 'live', codes: 'live', blog: 'live', wiki: 'live', sites: 'live',
billing: 'live', codes: 'live', blog: 'live', wiki: 'live', sites: 'live',
news: 'internal', reviews: 'internal', deals: 'internal', coupons: 'internal', trade: 'internal', host: 'internal',
news: 'internal', reviews: 'internal', deals: 'internal', coupons: 'internal', trade: 'internal', host: 'internal',
tips: 'internal', vip: 'internal', openre: 'internal', search: 'live', sources: 'internal', chat: 'internal', ai: 'internal',
tips: 'internal', vip: 'internal', openre: 'internal', search: 'live', sources: 'internal', chat: 'internal', ai: 'internal',
- sdk: 'library', shared: 'library', contracts: 'library',
+ sdk: 'library', shared: 'library', contracts: 'library', publishing: 'library',
examples: 'repository',
examples: 'repository',
realtime: 'placeholder',
realtime: 'placeholder',
};
};
@@ -101,7 +101,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], ch
@@ -101,7 +101,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], ch
assert.strictEqual(sBy.live.label, 'up');
assert.strictEqual(sBy.live.label, 'up');
assert.strictEqual(sBy.live.origin, 'https://openvibe.live');
assert.strictEqual(sBy.live.origin, 'https://openvibe.live');
assert.strictEqual(sBy.shared.label, `not running (library, released v${require('openvibe-shared/package.json').version})`);
assert.strictEqual(sBy.shared.label, `not running (library, released v${require('openvibe-shared/package.json').version})`);
- assert.deepStrictEqual(st.exposure_summary, { live: 13, internal: 12, library: 3, repository: 1, placeholder: 1 });
+ assert.deepStrictEqual(st.exposure_summary, { live: 13, internal: 12, library: 4, repository: 1, placeholder: 1 });
// The page: no row for a service whose domain serves a placeholder shows a bare "Up".
// The page: no row for a service whose domain serves a placeholder shows a bare "Up".
const html = await fetch(base + '/status').then(x => x.text());
const html = await fetch(base + '/status').then(x => x.text());
@@ -102,7 +102,7 @@ const rel = (contractsVersion, packages) => [200, { service: 'x', release: 'abcd
@@ -102,7 +102,7 @@ const rel = (contractsVersion, packages) => [200, { service: 'x', release: 'abcd
r = await get('/api/v1/registry/releases');
r = await get('/api/v1/registry/releases');
const rel2 = Object.fromEntries(r.body.services.map(s => [s.id, s]));
const rel2 = Object.fromEntries(r.body.services.map(s => [s.id, s]));
assert.ok(r.body.checked_at);
assert.ok(r.body.checked_at);
- assert.deepStrictEqual(r.body.libraries.map(l => l.id).sort(), ['contracts', 'sdk', 'shared']);
+ assert.deepStrictEqual(r.body.libraries.map(l => l.id).sort(), ['contracts', 'publishing', 'sdk', 'shared']);
assert.ok(!rel2.sdk && !rel2.contracts, 'libraries are not services');
assert.ok(!rel2.sdk && !rel2.contracts, 'libraries are not services');
assert.strictEqual(rel2.live.release, 'abcdef123456');
assert.strictEqual(rel2.live.release, 'abcdef123456');
assert.strictEqual(rel2.live.contracts_version, '0.28.0');
assert.strictEqual(rel2.live.contracts_version, '0.28.0');