openvibers/openvibe.network Commits

Network: Live may send a bound channel's chat robot commands to OpenVibe.Bot (grant bot.robot.control) (#174)

OP OpenVibers committed 14 Oct 2026 · 9a55896469 · 1 file, +4 −0

Plan T15 retires Live's own robot controls. A channel bound to an OpenVibe.Bot robot lets its chat drive the robot: OpenVibe.Chat relays `!forward`, `!say …` to Live (`POST /internal/chat-effects/hardware`), and Live forwards each one to Bot's `POST /api/v1/robots/:id/commands`. `X-OV-Subject` names the viewer, so Bot's roles, turn queue, cooldowns and e-stop judge it as that person.

That call needs Live's service token for audience `openvibe.bot` to carry `bot.robot.control`.

**Change**
- New default grant `['live', 'bot.robot.control', 'openvibe.bot', []]` in `server/identity/principals.js`. It is seeded at boot like the others; it is new, so no RESTORED_GRANTS entry is needed.
- `bot.robot.control` is an active, first-party, sensitive capability. Bot checks it, then applies its own gate for the subject named.

**Tests**
- `grants-admin`, `principals`, `service-principal` and `restored-grants` pass.

Changes

ddiff --git a/server/identity/principals.js b/server/identity/principals.js
iindex 69becd14..f5d4a403 100644
---- a/server/identity/principals.js
++++ b/server/identity/principals.js
@@@ -253,6 +253,10 @@ const DEFAULT_GRANTS = [
// (POST /api/v1/jobs, /jobs/:id/cancel, GET /jobs/:id, capability bot.job.dispatch, service-to-service only).
['run', 'events.event.publish', 'openvibe.events', []],
['run', 'bot.job.dispatch', 'openvibe.bot', []],
++ // Plan T15: a channel bound to an OpenVibe.Bot robot lets its chat drive it. Live forwards !forward, !say … to Bot's
++ // POST /api/v1/robots/:id/commands with X-OV-Subject naming the viewer, so Bot's roles, queue, cooldowns and e-stop
++ // judge each one as that person (Live has no robot code of its own since Live's controls were deleted).
++ ['live', 'bot.robot.control', 'openvibe.bot', []],
// Plan T2 lane B step 3: Host places and runs per project, so it reads a project's tenancy, placement and quotas.
// Other services get this row only when they ship a caller of GET /internal/projects/:project_id.
['host', 'network.project.read', SELF_AUDIENCE, []],