openvibers/openvibe.network Commits
Space's builder as the person's one agent: the grants between the two services (#634)
Changes
@@@ -6,7 +6,7 @@"runtime": "node 22, express, PostgreSQL 18 (openvibe-sdk/db, async) + Valkey 9","port": 4000,"domain": "openvibe.network (with auth., api., admin., my. and themes.openvibe.network in its manifest)",-- "contracts": "openvibe-contracts v0.320.0",++ "contracts": "openvibe-contracts v0.324.0","sdk": "openvibe-sdk v0.94.0","shared": "openvibe-shared v4.133.0","capabilities": [@@@ -50,7 +50,8 @@"per-actor limits (openvibe-sdk/limits)","universal telemetry: per-route aggregated platform.telemetry-sample@1 plus per-flush HTTP autoscaling gauges (openvibe-sdk/telemetry)","the Dock's state per person: GET and PUT /api/v1/me/dock (network.dock-state@1, network.dock-result@1)",-- "the machines hub (/api/v1/machines, plan L34): a person's machines, their roles and what they do right now, with Bot as the registry underneath (Network calls it as the person) and network.machine.online/offline/paired/roles_changed/revoked published through OpenVibe.Events"++ "the machines hub (/api/v1/machines, plan L34): a person's machines, their roles and what they do right now, with Bot as the registry underneath (Network calls it as the person) and network.machine.online/offline/paired/roles_changed/revoked published through OpenVibe.Events",++ "Space's builder as a task of the person's one agent (plan 2026-10-22 §Z stage Z1): the grants that let Space start a task of the person's agent on OpenVibe.Actor and let that agent work on the person's own spaces through OpenVibe.Space's builder API"],"updated": "2026-11-07"}@@@ -20,7 +20,7 @@"iovalkey": "^0.4.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.320.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.324.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.94.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.133.0","pg": "^8.23.0",@@@ -2303,9 +2303,9 @@}},"node_modules/openvibe-contracts": {-- "version": "0.320.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.320.0",-- "integrity": "sha512-ECShhtflTLicQosxVSEPlBFQn9eSnOUyO//Z2oS2NPscvb5RtZusrVq3xYWcJIs9XiJ7ws2Q9pAWPaRZIpEvwQ==",++ "version": "0.324.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.324.0",++ "integrity": "sha512-fSeabmHcX74SxVKEfGXMgWhEDugYQh5KWB+5Vj86CTvr2q50e/7AfHBDtVVw2WYQ06wiH1wN0hctDYoc2DQi9Q==","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -24,7 +24,7 @@"iovalkey": "^0.4.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.320.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.324.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.94.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.133.0","pg": "^8.23.0",@@@ -314,6 +314,14 @@ const DEFAULT_GRANTS = [// Account export and deletion again (Space#59): spaces hold personal data, so Space subscribes to the two account// events (its export part and deletion confirmation grants follow once its subscriptions answer).['space', 'events.subscription.manage', 'openvibe.events', []],++ // Plan 2026-10-22 section Z, stage Z1: Space's builder becomes a task of the person's one agent. Space asks++ // OpenVibe.Actor for a task on the person's behalf (X-OV-Subject: the task is theirs, their allowance and their++ // list, Space recorded as the service that started it) and reads that task back to follow it.++ ['space', 'actor.task.create', 'openvibe.actor', []],++ ['space', 'actor.task.read', 'openvibe.actor', []],++ // The other way round: the agent works on the person's own spaces through Space's builder API (X-OV-Subject names++ // the person; Space reads and writes only that person's spaces, and publishing waits for the person's answer).++ ...['space.build.read', 'space.build.create', 'space.build.write', 'space.build.publish'].map((c) => ['actor', c, 'openvibe.space', []]),// The hosting catalog (plan T20 step 3) reads OpenVibe.Sources items in category hosting (Host#69).['host', 'sources.item.read', 'openvibe.sources', []],// The Steam section of openvibe.games (the owner's valve.openvibe.games) reads OpenVibe.Sources items in categorynnew file mode 100644@@@ -0,0 +1,39 @@++/**++ * Space's builder as the person's one agent (plan 2026-10-22 §Z stage Z1): the grants that let the two services++ * work for the person.++ *++ * Space → OpenVibe.Actor actor.task.create, actor.task.read the task is the person's (X-OV-Subject)++ * Actor → OpenVibe.Space space.build.read|create|write|publish the agent works on the person's own spaces++ *++ * Each capability is one the pinned contracts offer, and it is active: a grant for a capability nobody knows++ * would be a grant that cannot be checked.++ *++ * node test/space-builder-grants.test.js++ */++'use strict';++const assert = require('assert');++const contracts = require('openvibe-contracts');++const principals = require('../server/identity/principals');++++const grants = principals.DEFAULT_GRANTS;++++// Space asks Actor for a task on the person's behalf, and reads it back to follow it.++for (const cap of ['actor.task.create', 'actor.task.read']) {++ const row = grants.find((g) => g[0] === 'space' && g[1] === cap && g[2] === 'openvibe.actor');++ assert.ok(row, `space holds ${cap} on openvibe.actor`);++ assert.deepStrictEqual(row[3], [], 'no namespace: the task is the person\'s, not a project\'s');++ const known = contracts.capabilities.get(cap);++ assert.ok(known && known.status === 'active', `${cap} is an active capability`);++}++++// The agent works on the person's own spaces through Space's builder API.++for (const cap of ['space.build.read', 'space.build.create', 'space.build.write', 'space.build.publish']) {++ const row = grants.find((g) => g[0] === 'actor' && g[1] === cap && g[2] === 'openvibe.space');++ assert.ok(row, `actor holds ${cap} on openvibe.space`);++ assert.deepStrictEqual(row[3], [], 'no namespace: the spaces are the person\'s own');++ const known = contracts.capabilities.get(cap);++ assert.ok(known && known.status === 'active', `${cap} is an active capability`);++ assert.strictEqual(known.owner, 'space', `${cap} belongs to space`);++}++++console.log('space builder grants: the task is the person\'s, the spaces are the person\'s own');
@@ -6,7 +6,7 @@
@@ -6,7 +6,7 @@
"runtime": "node 22, express, PostgreSQL 18 (openvibe-sdk/db, async) + Valkey 9",
"runtime": "node 22, express, PostgreSQL 18 (openvibe-sdk/db, async) + Valkey 9",
"port": 4000,
"port": 4000,
"domain": "openvibe.network (with auth., api., admin., my. and themes.openvibe.network in its manifest)",
"domain": "openvibe.network (with auth., api., admin., my. and themes.openvibe.network in its manifest)",
- "contracts": "openvibe-contracts v0.320.0",
+ "contracts": "openvibe-contracts v0.324.0",
"sdk": "openvibe-sdk v0.94.0",
"sdk": "openvibe-sdk v0.94.0",
"shared": "openvibe-shared v4.133.0",
"shared": "openvibe-shared v4.133.0",
"capabilities": [
"capabilities": [
@@ -50,7 +50,8 @@
@@ -50,7 +50,8 @@
"per-actor limits (openvibe-sdk/limits)",
"per-actor limits (openvibe-sdk/limits)",
"universal telemetry: per-route aggregated platform.telemetry-sample@1 plus per-flush HTTP autoscaling gauges (openvibe-sdk/telemetry)",
"universal telemetry: per-route aggregated platform.telemetry-sample@1 plus per-flush HTTP autoscaling gauges (openvibe-sdk/telemetry)",
"the Dock's state per person: GET and PUT /api/v1/me/dock (network.dock-state@1, network.dock-result@1)",
"the Dock's state per person: GET and PUT /api/v1/me/dock (network.dock-state@1, network.dock-result@1)",
- "the machines hub (/api/v1/machines, plan L34): a person's machines, their roles and what they do right now, with Bot as the registry underneath (Network calls it as the person) and network.machine.online/offline/paired/roles_changed/revoked published through OpenVibe.Events"
+ "the machines hub (/api/v1/machines, plan L34): a person's machines, their roles and what they do right now, with Bot as the registry underneath (Network calls it as the person) and network.machine.online/offline/paired/roles_changed/revoked published through OpenVibe.Events",
+ "Space's builder as a task of the person's one agent (plan 2026-10-22 §Z stage Z1): the grants that let Space start a task of the person's agent on OpenVibe.Actor and let that agent work on the person's own spaces through OpenVibe.Space's builder API"
],
],
"updated": "2026-11-07"
"updated": "2026-11-07"
}
}
@@ -20,7 +20,7 @@
@@ -20,7 +20,7 @@
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.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.320.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.324.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.94.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.94.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.133.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.133.0",
"pg": "^8.23.0",
"pg": "^8.23.0",
@@ -2303,9 +2303,9 @@
@@ -2303,9 +2303,9 @@
}
}
},
},
"node_modules/openvibe-contracts": {
"node_modules/openvibe-contracts": {
- "version": "0.320.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.320.0",
- "integrity": "sha512-ECShhtflTLicQosxVSEPlBFQn9eSnOUyO//Z2oS2NPscvb5RtZusrVq3xYWcJIs9XiJ7ws2Q9pAWPaRZIpEvwQ==",
+ "version": "0.324.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.324.0",
+ "integrity": "sha512-fSeabmHcX74SxVKEfGXMgWhEDugYQh5KWB+5Vj86CTvr2q50e/7AfHBDtVVw2WYQ06wiH1wN0hctDYoc2DQi9Q==",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -24,7 +24,7 @@
@@ -24,7 +24,7 @@
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.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.320.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.324.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.94.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.94.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.133.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.133.0",
"pg": "^8.23.0",
"pg": "^8.23.0",
@@ -314,6 +314,14 @@ const DEFAULT_GRANTS = [
@@ -314,6 +314,14 @@ const DEFAULT_GRANTS = [
// Account export and deletion again (Space#59): spaces hold personal data, so Space subscribes to the two account
// Account export and deletion again (Space#59): spaces hold personal data, so Space subscribes to the two account
// events (its export part and deletion confirmation grants follow once its subscriptions answer).
// events (its export part and deletion confirmation grants follow once its subscriptions answer).
['space', 'events.subscription.manage', 'openvibe.events', []],
['space', 'events.subscription.manage', 'openvibe.events', []],
+ // Plan 2026-10-22 section Z, stage Z1: Space's builder becomes a task of the person's one agent. Space asks
+ // OpenVibe.Actor for a task on the person's behalf (X-OV-Subject: the task is theirs, their allowance and their
+ // list, Space recorded as the service that started it) and reads that task back to follow it.
+ ['space', 'actor.task.create', 'openvibe.actor', []],
+ ['space', 'actor.task.read', 'openvibe.actor', []],
+ // The other way round: the agent works on the person's own spaces through Space's builder API (X-OV-Subject names
+ // the person; Space reads and writes only that person's spaces, and publishing waits for the person's answer).
+ ...['space.build.read', 'space.build.create', 'space.build.write', 'space.build.publish'].map((c) => ['actor', c, 'openvibe.space', []]),
// The hosting catalog (plan T20 step 3) reads OpenVibe.Sources items in category hosting (Host#69).
// The hosting catalog (plan T20 step 3) reads OpenVibe.Sources items in category hosting (Host#69).
['host', 'sources.item.read', 'openvibe.sources', []],
['host', 'sources.item.read', 'openvibe.sources', []],
// The Steam section of openvibe.games (the owner's valve.openvibe.games) reads OpenVibe.Sources items in category
// The Steam section of openvibe.games (the owner's valve.openvibe.games) reads OpenVibe.Sources items in category
new file mode 100644
new file mode 100644
@@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
+/**
+ * Space's builder as the person's one agent (plan 2026-10-22 §Z stage Z1): the grants that let the two services
+ * work for the person.
+ *
+ * Space → OpenVibe.Actor actor.task.create, actor.task.read the task is the person's (X-OV-Subject)
+ * Actor → OpenVibe.Space space.build.read|create|write|publish the agent works on the person's own spaces
+ *
+ * Each capability is one the pinned contracts offer, and it is active: a grant for a capability nobody knows
+ * would be a grant that cannot be checked.
+ *
+ * node test/space-builder-grants.test.js
+ */
+'use strict';
+const assert = require('assert');
+const contracts = require('openvibe-contracts');
+const principals = require('../server/identity/principals');
+
+const grants = principals.DEFAULT_GRANTS;
+
+// Space asks Actor for a task on the person's behalf, and reads it back to follow it.
+for (const cap of ['actor.task.create', 'actor.task.read']) {
+ const row = grants.find((g) => g[0] === 'space' && g[1] === cap && g[2] === 'openvibe.actor');
+ assert.ok(row, `space holds ${cap} on openvibe.actor`);
+ assert.deepStrictEqual(row[3], [], 'no namespace: the task is the person\'s, not a project\'s');
+ const known = contracts.capabilities.get(cap);
+ assert.ok(known && known.status === 'active', `${cap} is an active capability`);
+}
+
+// The agent works on the person's own spaces through Space's builder API.
+for (const cap of ['space.build.read', 'space.build.create', 'space.build.write', 'space.build.publish']) {
+ const row = grants.find((g) => g[0] === 'actor' && g[1] === cap && g[2] === 'openvibe.space');
+ assert.ok(row, `actor holds ${cap} on openvibe.space`);
+ assert.deepStrictEqual(row[3], [], 'no namespace: the spaces are the person\'s own');
+ const known = contracts.capabilities.get(cap);
+ assert.ok(known && known.status === 'active', `${cap} is an active capability`);
+ assert.strictEqual(known.owner, 'space', `${cap} belongs to space`);
+}
+
+console.log('space builder grants: the task is the person\'s, the spaces are the person\'s own');