openvibers/openvibe.services Commits
openvibe-contracts v0.150.0 (was v0.143.0) (#49)
Changes
@@@ -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 -->@@@ -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"}@@@ -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",@@@ -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"@@@ -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' },];@@@ -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`);}@@@ -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'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();
@@ -102,7 +102,7 @@ There is no polling loop: the index reads the authorities on each request (step
@@ -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.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`).
- **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).
- **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).
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).
@@ -144,7 +144,7 @@ one receipt per export and deletion in `account_data_events` (migration 0003).
- The resource index's owner becomes NULL.
- 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 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.
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).
@@ -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).
Part of the [OpenVibe network](https://openvibe.network). Built in the open by [OpenVibers](https://github.com/OpenVibers).
<!-- versions:start -->
<!-- versions:start -->
-- openvibe-contracts: v0.143.0
+- openvibe-contracts: v0.150.0
- openvibe-sdk: v0.44.0
- openvibe-sdk: v0.44.0
- openvibe-shared: v4.0.2
- openvibe-shared: v4.0.2
<!-- versions:end -->
<!-- versions:end -->
@@ -7,11 +7,11 @@
@@ -7,11 +7,11 @@
"runtime": "node 22, express 4, PostgreSQL 18 (openvibe-sdk/db)",
"runtime": "node 22, express 4, PostgreSQL 18 (openvibe-sdk/db)",
"port": 4930,
"port": 4930,
"domain": "openvibe.services",
"domain": "openvibe.services",
- "contracts": "openvibe-contracts v0.143.0",
+ "contracts": "openvibe-contracts v0.150.0",
"sdk": "openvibe-sdk v0.44.0",
"sdk": "openvibe-sdk v0.44.0",
"shared": "openvibe-shared v4.0.2",
"shared": "openvibe-shared v4.0.2",
"packages": [
"packages": [
- "openvibe-contracts v0.143.0",
+ "openvibe-contracts v0.150.0",
"openvibe-sdk v0.44.0",
"openvibe-sdk v0.44.0",
"openvibe-shared v4.0.2"
"openvibe-shared v4.0.2"
],
],
@@ -63,5 +63,5 @@
@@ -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",
"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)"
"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"
}
}
@@ -16,7 +16,7 @@
@@ -16,7 +16,7 @@
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
"helmet": "^8.1.0",
"iovalkey": "^0.4.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-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",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.0.2",
"pg": "^8.23.0"
"pg": "^8.23.0"
@@ -890,9 +890,9 @@
@@ -890,9 +890,9 @@
}
}
},
},
"node_modules/openvibe-contracts": {
"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",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -27,7 +27,7 @@
@@ -27,7 +27,7 @@
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
"helmet": "^8.1.0",
"iovalkey": "^0.4.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-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",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.0.2",
"pg": "^8.23.0"
"pg": "^8.23.0"
@@ -10,8 +10,8 @@
@@ -10,8 +10,8 @@
/** The services that publish /limits.json, with where Services reads them and where people can. */
/** The services that publish /limits.json, with where Services reads them and where people can. */
function limitSources(env = process.env) {
function limitSources(env = process.env) {
return [
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: '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' },
{ service: 'media', name: 'OpenVibe Media', internal: env.OV_MEDIA_INTERNAL_URL || 'http://127.0.0.1:4100', public: 'https://openvibe.media/limits.json' },
];
];
@@ -17,13 +17,15 @@ const { createAuthorities } = require('../server/authorities');
@@ -17,13 +17,15 @@ const { createAuthorities } = require('../server/authorities');
const { check, done } = require('./helpers/app');
const { check, done } = require('./helpers/app');
const quiet = { warn() {}, log() {}, error() {} };
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() {
async function main() {
await check('the pin lists eleven active <id>.resource.read capabilities, ten of them authorities', () => {
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();
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');
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).
// 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'));
assert.ok(!createAuthorities(loadConfig({}), { log: quiet }).ids().includes('services'));
@@ -36,7 +38,7 @@ async function main() {
@@ -36,7 +38,7 @@ async function main() {
network: 'http://127.0.0.1:4000',
network: 'http://127.0.0.1:4000',
media: 'http://127.0.0.1:4100',
media: 'http://127.0.0.1:4100',
events: 'http://127.0.0.1:4300',
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',
watch: 'http://127.0.0.1:4730',
actor: 'http://127.0.0.1:4950',
actor: 'http://127.0.0.1:4950',
bot: 'http://127.0.0.1:4630',
bot: 'http://127.0.0.1:4630',
@@ -60,7 +62,7 @@ async function main() {
@@ -60,7 +62,7 @@ async function main() {
await check('Services, placeholders and services without the capability are not authorities', () => {
await check('Services, placeholders and services without the capability are not authorities', () => {
const registry = createAuthorities(loadConfig({}), { log: quiet });
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;
if (!contracts.services.get(id)) continue;
assert.strictEqual(registry.get(id), null, `${id} must not be an authority`);
assert.strictEqual(registry.get(id), null, `${id} must not be an authority`);
}
}
@@ -8,12 +8,12 @@
@@ -8,12 +8,12 @@
const assert = require('assert');
const assert = require('assert');
const http = require('http');
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: [
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 = {
const EVENTS = {
@@ -45,9 +45,9 @@ function serve(body) {
@@ -45,9 +45,9 @@ function serve(body) {
}
}
(async () => {
(async () => {
- const host = await serve(HOST);
+ const space = await serve(SPACE);
const events = await serve(EVENTS);
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}`;
process.env.OV_EVENTS_INTERNAL_URL = `http://127.0.0.1:${events.address().port}`;
const media = await serve(MEDIA);
const media = await serve(MEDIA);
process.env.OV_MEDIA_INTERNAL_URL = `http://127.0.0.1:${media.address().port}`;
process.env.OV_MEDIA_INTERNAL_URL = `http://127.0.0.1:${media.address().port}`;
@@ -58,10 +58,10 @@ function serve(body) {
@@ -58,10 +58,10 @@ function serve(body) {
const r = await t.get('/docs/limits');
const r = await t.get('/docs/limits');
assert.strictEqual(r.status, 200, r.text.slice(0, 300));
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',
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.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.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(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');
assert.ok(!/Other capabilities[\s\S]*media\.object\.upload \(/.test(r.text), 'Media publishes its own now');
@@ -78,12 +78,12 @@ function serve(body) {
@@ -78,12 +78,12 @@ function serve(body) {
assert.strictEqual(r.status, 200);
assert.strictEqual(r.status, 200);
assert.ok(r.text.includes('OpenVibe Events'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('OpenVibe Events'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('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 t2.close();
});
});
await t.close();
await t.close();
- host.close();
+ space.close();
events.close();
events.close();
media.close();
media.close();
done();
done();