openvibers/openvibe.services Commits

openvibe-contracts v0.150.0 (was v0.143.0) (#49)

OP OpenVibers committed 12 Oct 2026 · 1838d4d12d · 7 files, +31 −29

* openvibe-contracts v0.150.0 (was v0.143.0)

Opened by scripts/pin-bump.js in OpenVibers/OpenVibe.Shared after the v0.150.0 release.

* Services: Space is the static hosting authority and limits source (contracts 0.150.0); Host is not

Changes

ddiff --git a/README.md b/README.md
iindex 32c915d..de452b3 100644
---- a/README.md
++++ b/README.md
@@@ -102,7 +102,7 @@ There is no polling loop: the index reads the authorities on each request (step
- **OpenVibe.Events** — the outbox relay publishes `services.app.*` with Services' service token (`events.event.publish`); the Events playground calls it with the app's token (`events.app.publish`); the project archive pulls the project's app events with a Network export token (`events.app.read`).
- **OpenVibe.Media** — the Media playground uploads with the app's token into the project's namespace; the project archive lists the project's objects, namespaces and download URLs with a Network export token (`media.object.list`, `media.object.read`).
- **PostgreSQL 18 and Valkey 9** (OpenVibe.Host `roles/data/`, ADR-035): every read and write is async through `openvibe-sdk/db`; Valkey holds the per-actor limit counters (optional).
--- **openvibe-contracts v0.143.0**, **openvibe-sdk v0.35.1**, **openvibe-shared v4.0.2** (pinned tag tarballs; the docs show the tag and the package version, and say so when they differ).
++- **openvibe-contracts v0.150.0**, **openvibe-sdk v0.35.1**, **openvibe-shared v4.0.2** (pinned tag tarballs; the docs show the tag and the package version, and say so when they differ).
No path in Services sends or accepts a shared loopback key (tested by grep and at runtime).
@@@ -144,7 +144,7 @@ one receipt per export and deletion in `account_data_events` (migration 0003).
- The resource index's owner becomes NULL.
- The append-only release log, the control log and the staff trust tiers stay attributed, for accountability.
--The Services service manifest, `services.release.manage|read`, `services.resource.read` and `services.app-manifest@1` are released in openvibe-contracts v0.143.0 (they replaced the `codes.*` forms, retired that release); the CI contracts check is blocking.
++The Services service manifest, `services.release.manage|read`, `services.resource.read` and `services.app-manifest@1` are released in openvibe-contracts v0.150.0 (they replaced the `codes.*` forms, retired that release); the CI contracts check is blocking.
For the playgrounds to succeed end to end (not Services' code; configuration elsewhere): Network `DEV_SANDBOX_AUDIENCES` including `openvibe.media` and `openvibe.events`, a staff-set allowance containing `media.object.upload` and `events.app.publish` for the project, a Media tenant keyed by the project id, and OpenVibe.Events serving `events.app.publish` for app tokens. On 2026-09-23 these were in place on production: a sandbox app uploaded to Media (tenant `prj_…-sandbox`) and published and read app events through public endpoints. That run used curl, not the playgrounds, and it is not yet a committed, repeatable check.
@@@ -251,7 +251,7 @@ Reporting a vulnerability: [SECURITY.md](SECURITY.md).
Part of the [OpenVibe network](https://openvibe.network). Built in the open by [OpenVibers](https://github.com/OpenVibers).
<!-- versions:start -->
--- openvibe-contracts: v0.143.0
++- openvibe-contracts: v0.150.0
- openvibe-sdk: v0.44.0
- openvibe-shared: v4.0.2
<!-- versions:end -->
ddiff --git a/STATUS.json b/STATUS.json
iindex 1edc66d..e9338ea 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -7,11 +7,11 @@
"runtime": "node 22, express 4, PostgreSQL 18 (openvibe-sdk/db)",
"port": 4930,
"domain": "openvibe.services",
-- "contracts": "openvibe-contracts v0.143.0",
++ "contracts": "openvibe-contracts v0.150.0",
"sdk": "openvibe-sdk v0.44.0",
"shared": "openvibe-shared v4.0.2",
"packages": [
-- "openvibe-contracts v0.143.0",
++ "openvibe-contracts v0.150.0",
"openvibe-sdk v0.44.0",
"openvibe-shared v4.0.2"
],
@@@ -63,5 +63,5 @@
"Governance material (code of conduct, contribution guide, contributor ladder, moderation policy) is written as drafts pending owner review: not in effect, not indexed",
"plan T13 step 4 done in OpenVibe.Contracts v0.110.0 (services.resource.read is registered, active and first-party), step 5 done in Network, step 7 (read model + ingestion), steps 9-15 (console, control operations, recipes, usage, deploy)"
],
-- "updated": "2026-10-11"
++ "updated": "2026-10-12"
}
ddiff --git a/package-lock.json b/package-lock.json
iindex b1d5055..2c2b8fb 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -16,7 +16,7 @@
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
"iovalkey": "^0.4.0",
-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.143.0",
++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.150.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.44.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.0.2",
"pg": "^8.23.0"
@@@ -890,9 +890,9 @@
}
},
"node_modules/openvibe-contracts": {
-- "version": "0.143.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.143.0",
-- "integrity": "sha512-Cdbf+9RoPAUBibpM2zXQvtsiKMugb55dKubTt8pFZvAmxsLtGvX+zaC++yL1vOSub8FX3+GgqcGtoJS6tklrUg==",
++ "version": "0.150.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.150.0",
++ "integrity": "sha512-CNKm1UpEynr56yIK9ppnpSAM2JXT+YLuPbLGzw4tB9A3pX/rfSIKlo8Pj3agEkoNwwlavXDdXv9az1S0Gg2H7g==",
"license": "AGPL-3.0-only",
"dependencies": {
"ajv": "^8.17.1",
ddiff --git a/package.json b/package.json
iindex 34fea1f..6cafdf6 100644
---- a/package.json
++++ b/package.json
@@@ -27,7 +27,7 @@
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
"iovalkey": "^0.4.0",
-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.143.0",
++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.150.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.44.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.0.2",
"pg": "^8.23.0"
ddiff --git a/server/domain/limits.js b/server/domain/limits.js
iindex 67ea169..d021ffb 100644
---- a/server/domain/limits.js
++++ b/server/domain/limits.js
@@@ -10,8 +10,8 @@
/** The services that publish /limits.json, with where Services reads them and where people can. */
function limitSources(env = process.env) {
return [
-- // public: null until openvibe.host serves Host itself (WS-N task 10, Stage B step 2); read internally meanwhile.
-- { service: 'host', name: 'OpenVibe Host', internal: env.OV_HOST_INTERNAL_URL || 'http://127.0.0.1:4910', public: null },
++ // Static hosting's quotas: OpenVibe.Space since the engine moved from Host (plan T20, 2026-10-12).
++ { service: 'space', name: 'OpenVibe Space', internal: env.OV_SPACE_INTERNAL_URL || 'http://127.0.0.1:4940', public: 'https://openvibe.space/limits.json' },
{ service: 'events', name: 'OpenVibe Events', internal: env.OV_EVENTS_INTERNAL_URL || 'http://127.0.0.1:4300', public: 'https://openvibe.events/limits.json' },
{ service: 'media', name: 'OpenVibe Media', internal: env.OV_MEDIA_INTERNAL_URL || 'http://127.0.0.1:4100', public: 'https://openvibe.media/limits.json' },
];
ddiff --git a/test/authorities.test.js b/test/authorities.test.js
iindex ee917fd..c5c7af6 100644
---- a/test/authorities.test.js
++++ b/test/authorities.test.js
@@@ -17,13 +17,15 @@ const { createAuthorities } = require('../server/authorities');
const { check, done } = require('./helpers/app');
const quiet = { warn() {}, log() {}, error() {} };
--const AUTHORITIES = ['actor', 'bot', 'chat', 'community', 'events', 'host', 'media', 'network', 'openre', 'watch'];
++// Static hosting's index is Space's since contracts 0.150.0 (the engine moved from Host, plan T20).
++const AUTHORITIES = ['actor', 'bot', 'chat', 'community', 'events', 'media', 'network', 'openre', 'space', 'watch'];
async function main() {
await check('the pin lists eleven active <id>.resource.read capabilities, ten of them authorities', () => {
const ids = contracts.capabilities.manifests.filter((c) => /^([a-z][a-z0-9-]{0,31})\.resource\.read$/.test(c.id) && c.status === 'active').map((c) => c.id).sort();
-- assert.deepStrictEqual(ids, ['actor.resource.read', 'bot.resource.read', 'chat.resource.read', 'community.resource.read', 'events.resource.read', 'host.resource.read',
-- 'media.resource.read', 'network.resource.read', 'openre.resource.read', 'services.resource.read', 'watch.resource.read']);
++ assert.deepStrictEqual(ids, ['actor.resource.read', 'bot.resource.read', 'chat.resource.read', 'community.resource.read', 'events.resource.read',
++ 'media.resource.read', 'network.resource.read', 'openre.resource.read', 'services.resource.read', 'space.resource.read', 'watch.resource.read']);
++ assert.strictEqual(contracts.capabilities.get('host.resource.read').status, 'retired', 'Host serves no hosting index since 0.150.0');
assert.strictEqual(contracts.capabilities.get('codes.resource.read').status, 'retired', 'Codes owns no resources since 0.113.0');
// services.resource.read is Services' own id; Services is never its own authority (the next check).
assert.ok(!createAuthorities(loadConfig({}), { log: quiet }).ids().includes('services'));
@@@ -36,7 +38,7 @@ async function main() {
network: 'http://127.0.0.1:4000',
media: 'http://127.0.0.1:4100',
events: 'http://127.0.0.1:4300',
-- host: 'http://127.0.0.1:4910',
++ space: 'http://127.0.0.1:4940',
watch: 'http://127.0.0.1:4730',
actor: 'http://127.0.0.1:4950',
bot: 'http://127.0.0.1:4630',
@@@ -60,7 +62,7 @@ async function main() {
await check('Services, placeholders and services without the capability are not authorities', () => {
const registry = createAuthorities(loadConfig({}), { log: quiet });
-- for (const id of ['services', 'codes', 'run', 'space', 'tools', 'actor-console', 'sites']) {
++ for (const id of ['services', 'codes', 'run', 'host', 'tools', 'actor-console', 'sites']) {
if (!contracts.services.get(id)) continue;
assert.strictEqual(registry.get(id), null, `${id} must not be an authority`);
}
ddiff --git a/test/docs-limits.test.js b/test/docs-limits.test.js
iindex c7d5932..becbe68 100644
---- a/test/docs-limits.test.js
++++ b/test/docs-limits.test.js
@@@ -8,12 +8,12 @@
const assert = require('assert');
const http = require('http');
--const HOST = {
-- service: 'host', scope: 'per project and environment; projects per owner',
++const SPACE = {
++ service: 'space', scope: 'per project and environment; projects per owner',
limits: [
-- { id: 'custom_domains', label: 'Custom domains', capability: 'host.site.manage', unit: 'count', production: 5, sandbox: 0, exceeded: '429 quota.custom_domains' },
-- { id: 'storage_bytes', label: 'Stored bytes (every deploy kept)', capability: 'host.site.manage', unit: 'bytes', production: 1073741824, sandbox: 104857600, exceeded: '413 quota.storage' },
-- { id: 'deploys_per_day', label: 'Deploys in 24 hours', capability: 'host.site.manage', unit: 'per_day', production: 50, sandbox: 20, exceeded: '429 quota.deploys_per_day' },
++ { id: 'custom_domains', label: 'Custom domains', capability: 'space.site.manage', unit: 'count', production: 5, sandbox: 0, exceeded: '429 quota.custom_domains' },
++ { id: 'storage_bytes', label: 'Stored bytes (every deploy kept)', capability: 'space.site.manage', unit: 'bytes', production: 1073741824, sandbox: 104857600, exceeded: '413 quota.storage' },
++ { id: 'deploys_per_day', label: 'Deploys in 24 hours', capability: 'space.site.manage', unit: 'per_day', production: 50, sandbox: 20, exceeded: '429 quota.deploys_per_day' },
],
};
const EVENTS = {
@@@ -45,9 +45,9 @@ function serve(body) {
}
(async () => {
-- const host = await serve(HOST);
++ const space = await serve(SPACE);
const events = await serve(EVENTS);
-- process.env.OV_HOST_INTERNAL_URL = `http://127.0.0.1:${host.address().port}`;
++ process.env.OV_SPACE_INTERNAL_URL = `http://127.0.0.1:${space.address().port}`;
process.env.OV_EVENTS_INTERNAL_URL = `http://127.0.0.1:${events.address().port}`;
const media = await serve(MEDIA);
process.env.OV_MEDIA_INTERNAL_URL = `http://127.0.0.1:${media.address().port}`;
@@@ -58,10 +58,10 @@ function serve(body) {
const r = await t.get('/docs/limits');
assert.strictEqual(r.status, 200, r.text.slice(0, 300));
for (const s of ['1 GB', '100 MB', '50 in 24 hours', '120 a minute', '30 a minute', '7 days', 'quota.custom_domains', 'events.quota_exceeded',
-- 'openvibe.events/limits.json', 'openvibe.media/limits.json', '72 hours', 'media.quota.exceeded', 'per project and environment (ADR-014)']) {
++ 'openvibe.events/limits.json', 'openvibe.media/limits.json', 'openvibe.space/limits.json', '72 hours', 'media.quota.exceeded', 'per project and environment (ADR-014)']) {
assert.ok(r.text.includes(s), `the page says ${s}`);
}
-- assert.match(r.text, /Custom domains<\/td><td[^>]*>[^<]*<a[^>]*><code>host\.site\.manage<\/code><\/a><\/td><td[^>]*>none<\/td>/, 'sandbox 0 reads "none"');
++ assert.match(r.text, /Custom domains<\/td><td[^>]*>[^<]*<a[^>]*><code>space\.site\.manage<\/code><\/a><\/td><td[^>]*>none<\/td>/, 'sandbox 0 reads "none"');
assert.match(r.text, /Webhook subscriptions.*?<td[^>]*>5<\/td><td[^>]*>no limit<\/td>/s, 'production null reads "no limit"');
assert.ok(r.text.includes('Other capabilities') && r.text.includes('/docs/capabilities/tools.tool.run'), 'owners without /limits.json are listed');
assert.ok(!/Other capabilities[\s\S]*media\.object\.upload \(/.test(r.text), 'Media publishes its own now');
@@@ -78,12 +78,12 @@ function serve(body) {
assert.strictEqual(r.status, 200);
assert.ok(r.text.includes('OpenVibe Events&#39;s limits could not be read') || r.text.includes("OpenVibe Events's limits could not be read"), 'the problem is shown');
assert.ok(!r.text.includes('120 a minute'), 'no Events numbers without Events');
-- assert.ok(r.text.includes('50 in 24 hours'), 'Host still shows');
++ assert.ok(r.text.includes('50 in 24 hours'), 'Space still shows');
await t2.close();
});
await t.close();
-- host.close();
++ space.close();
events.close();
media.close();
done();