openvibers/openvibe.contracts Commits

Add the bot service lifecycle manifest (#8)

OP OpenVibers committed 4 Oct 2026 · e2066ffb07 · 11 files, +361 −364

local checks ✓ 3/3 · CI ✓ 3/3 · review SHIP by DeepSeek harness · 3 fix rounds

https://github.com/OpenVibers/OpenVibe.Contracts/pull/8

Changes

ddiff --git a/contracts/bot/robot-manage-result.v1.json b/contracts/bot/robot-manage-result.v1.json
iindex 4ab1379d..e59e313d 100644
---- a/contracts/bot/robot-manage-result.v1.json
++++ b/contracts/bot/robot-manage-result.v1.json
@@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/bot/robot-manage-result.v1.json",
"title": "BotRobotManageResult",
-- "description": "bot.robot-manage-result@1: answers of bot.robot.manage on OpenVibe.Bot. POST /api/v1/robots → 201 { robot, pairing }; PATCH …/robots/:id and POST …/robots/:id/estop/clear → { robot }; DELETE …/robots/:id → 204 (no body); POST …/robots/:id/pairing-code → 201 { code, expires_at, installer }; POST …/robots/:id/operators → 201 { operators }; DELETE …/robots/:id/operators/:subject → { operators }; GET …/robots/:id/audit?limit=&before= → { audit, next_before } (newest first); POST …/devices/:id/rotate and /revoke → bot.device-connect-result@1.",
++ "description": "bot.robot-manage-result@1: answers of bot.robot.manage on OpenVibe.Bot. POST /api/v1/robots → 201 { robot, pairing }; PATCH …/robots/:id and POST …/robots/:id/estop/clear → { robot }; DELETE …/robots/:id → 204 (no body); POST …/robots/:id/pairing-code → 201 { code, expires_at, installer }; POST …/robots/:id/operators → 201 { operators }; DELETE …/robots/:id/operators/:subject → { operators }; POST …/devices/:id/rotate and /revoke → bot.device-connect-result@1.",
"anyOf": [
{
"type": "object",
@@@ -118,99 +118,6 @@
},
{
"$ref": "device-connect-result.v1.json"
-- },
-- {
-- "type": "object",
-- "required": [
-- "audit",
-- "next_before"
-- ],
-- "additionalProperties": false,
-- "properties": {
-- "next_before": {
-- "type": [
-- "integer",
-- "null"
-- ]
-- },
-- "audit": {
-- "type": "array",
-- "items": {
-- "type": "object",
-- "required": [
-- "id",
-- "robot_id",
-- "kind",
-- "result",
-- "at"
-- ],
-- "properties": {
-- "id": {
-- "type": "integer"
-- },
-- "robot_id": {
-- "type": "string",
-- "pattern": "^rob_[0-9A-HJKMNP-TV-Z]{26}$"
-- },
-- "device_id": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "operator_subject": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "operator_kind": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "role": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "kind": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "value": {},
-- "result": {
-- "enum": [
-- "ack",
-- "nack",
-- "refused",
-- "expired"
-- ]
-- },
-- "reason": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "latency_ms": {
-- "type": [
-- "integer",
-- "null"
-- ]
-- },
-- "at": {
-- "type": "string",
-- "format": "date-time"
-- }
-- }
-- }
-- }
-- }
}
]
}
ddiff --git a/contracts/bot/robot-read-result.v1.json b/contracts/bot/robot-read-result.v1.json
iindex 2ce42e82..48c65348 100644
---- a/contracts/bot/robot-read-result.v1.json
++++ b/contracts/bot/robot-read-result.v1.json
@@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/bot/robot-read-result.v1.json",
"title": "BotRobotReadResult",
-- "description": "bot.robot-read-result@1: answers of bot.robot.read on OpenVibe.Bot. GET /api/v1/robots?owner= → { robots }; GET …/robots/:id → { robot, role } (role \"service\" for a service token); GET …/robots/:id/operators → { operators }; GET …/robots/:id/devices → { devices } (no hashes, with online).",
++ "description": "bot.robot-read-result@1: answers of bot.robot.read on OpenVibe.Bot (a service token needs no acting subject: only GET …/robots needs ?owner=). GET /api/v1/robots?owner= → { robots }; GET …/robots/:id → { robot, role }; GET …/robots/:id/operators → { operators }; GET …/robots/:id/devices → { devices } (no hashes, with online); GET …/robots/:id/audit?limit=&before= → { audit, next_before } (newest first).",
"anyOf": [
{
"type": "object",
@@@ -92,6 +92,99 @@
}
}
}
++ },
++ {
++ "type": "object",
++ "required": [
++ "audit",
++ "next_before"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "next_before": {
++ "type": [
++ "integer",
++ "null"
++ ]
++ },
++ "audit": {
++ "type": "array",
++ "items": {
++ "type": "object",
++ "required": [
++ "id",
++ "robot_id",
++ "kind",
++ "result",
++ "at"
++ ],
++ "properties": {
++ "id": {
++ "type": "integer"
++ },
++ "robot_id": {
++ "type": "string",
++ "pattern": "^rob_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "device_id": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "operator_subject": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "operator_kind": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "role": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "kind": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "value": {},
++ "result": {
++ "enum": [
++ "ack",
++ "nack",
++ "refused",
++ "expired"
++ ]
++ },
++ "reason": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "latency_ms": {
++ "type": [
++ "integer",
++ "null"
++ ]
++ },
++ "at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++ }
++ }
++ }
}
]
}
ddiff --git a/fixtures/bot.robot-manage-result/invalid/bad-audit-result.json b/fixtures/bot.robot-manage-result/invalid/bad-audit-result.json
ddeleted file mode 100644
iindex 33594738..00000000
---- a/fixtures/bot.robot-manage-result/invalid/bad-audit-result.json
++++ /dev/null
@@@ -1,12 +0,0 @@
--{
-- "audit": [
-- {
-- "id": 1,
-- "robot_id": "rob_01J8Z4M2Q0R7T9YV3K6N8P1W2X",
-- "kind": "drive",
-- "result": "maybe",
-- "at": "2026-09-29T19:20:00.000Z"
-- }
-- ],
-- "next_before": null
--}
ddiff --git a/fixtures/bot.robot-manage-result/valid/audit.json b/fixtures/bot.robot-manage-result/valid/audit.json
ddeleted file mode 100644
iindex d3d5b168..00000000
---- a/fixtures/bot.robot-manage-result/valid/audit.json
++++ /dev/null
@@@ -1,21 +0,0 @@
--{
-- "audit": [
-- {
-- "id": 42,
-- "robot_id": "rob_01J8Z4M2Q0R7T9YV3K6N8P1W2X",
-- "device_id": "dev_01J8Z4N0A1B2C3D4E5F6G7H8J9",
-- "operator_subject": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
-- "operator_kind": "user",
-- "role": "operator",
-- "kind": "drive",
-- "value": {
-- "throttle": 0.6
-- },
-- "result": "ack",
-- "reason": null,
-- "latency_ms": 42,
-- "at": "2026-09-29T19:20:00.000Z"
-- }
-- ],
-- "next_before": 42
--}
ddiff --git a/fixtures/bot.robot-read-result/invalid/bad-audit-result.json b/fixtures/bot.robot-read-result/invalid/bad-audit-result.json
nnew file mode 100644
iindex 00000000..33594738
---- /dev/null
++++ b/fixtures/bot.robot-read-result/invalid/bad-audit-result.json
@@@ -0,0 +1,12 @@
++{
++ "audit": [
++ {
++ "id": 1,
++ "robot_id": "rob_01J8Z4M2Q0R7T9YV3K6N8P1W2X",
++ "kind": "drive",
++ "result": "maybe",
++ "at": "2026-09-29T19:20:00.000Z"
++ }
++ ],
++ "next_before": null
++}
ddiff --git a/fixtures/bot.robot-read-result/valid/audit.json b/fixtures/bot.robot-read-result/valid/audit.json
nnew file mode 100644
iindex 00000000..d3d5b168
---- /dev/null
++++ b/fixtures/bot.robot-read-result/valid/audit.json
@@@ -0,0 +1,21 @@
++{
++ "audit": [
++ {
++ "id": 42,
++ "robot_id": "rob_01J8Z4M2Q0R7T9YV3K6N8P1W2X",
++ "device_id": "dev_01J8Z4N0A1B2C3D4E5F6G7H8J9",
++ "operator_subject": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
++ "operator_kind": "user",
++ "role": "operator",
++ "kind": "drive",
++ "value": {
++ "throttle": 0.6
++ },
++ "result": "ack",
++ "reason": null,
++ "latency_ms": 42,
++ "at": "2026-09-29T19:20:00.000Z"
++ }
++ ],
++ "next_before": 42
++}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex 82ec5af3..bdd6dd97 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -62576,7 +62576,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/bot/robot-read-result.v1.json",
"title": "BotRobotReadResult",
-- "description": "bot.robot-read-result@1: answers of bot.robot.read on OpenVibe.Bot. GET /api/v1/robots?owner= → { robots }; GET …/robots/:id → { robot, role } (role \"service\" for a service token); GET …/robots/:id/operators → { operators }; GET …/robots/:id/devices → { devices } (no hashes, with online).",
++ "description": "bot.robot-read-result@1: answers of bot.robot.read on OpenVibe.Bot (a service token needs no acting subject: only GET …/robots needs ?owner=). GET /api/v1/robots?owner= → { robots }; GET …/robots/:id → { robot, role }; GET …/robots/:id/operators → { operators }; GET …/robots/:id/devices → { devices } (no hashes, with online); GET …/robots/:id/audit?limit=&before= → { audit, next_before } (newest first).",
"anyOf": [
{
"type": "object",
@@@ -62666,6 +62666,99 @@
}
}
}
++ },
++ {
++ "type": "object",
++ "required": [
++ "audit",
++ "next_before"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "next_before": {
++ "type": [
++ "integer",
++ "null"
++ ]
++ },
++ "audit": {
++ "type": "array",
++ "items": {
++ "type": "object",
++ "required": [
++ "id",
++ "robot_id",
++ "kind",
++ "result",
++ "at"
++ ],
++ "properties": {
++ "id": {
++ "type": "integer"
++ },
++ "robot_id": {
++ "type": "string",
++ "pattern": "^rob_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "device_id": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "operator_subject": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "operator_kind": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "role": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "kind": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "value": {},
++ "result": {
++ "enum": [
++ "ack",
++ "nack",
++ "refused",
++ "expired"
++ ]
++ },
++ "reason": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "latency_ms": {
++ "type": [
++ "integer",
++ "null"
++ ]
++ },
++ "at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++ }
++ }
++ }
}
]
}
@@@ -62762,7 +62855,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/bot/robot-manage-result.v1.json",
"title": "BotRobotManageResult",
-- "description": "bot.robot-manage-result@1: answers of bot.robot.manage on OpenVibe.Bot. POST /api/v1/robots → 201 { robot, pairing }; PATCH …/robots/:id and POST …/robots/:id/estop/clear → { robot }; DELETE …/robots/:id → 204 (no body); POST …/robots/:id/pairing-code → 201 { code, expires_at, installer }; POST …/robots/:id/operators → 201 { operators }; DELETE …/robots/:id/operators/:subject → { operators }; GET …/robots/:id/audit?limit=&before= → { audit, next_before } (newest first); POST …/devices/:id/rotate and /revoke → bot.device-connect-result@1.",
++ "description": "bot.robot-manage-result@1: answers of bot.robot.manage on OpenVibe.Bot. POST /api/v1/robots → 201 { robot, pairing }; PATCH …/robots/:id and POST …/robots/:id/estop/clear → { robot }; DELETE …/robots/:id → 204 (no body); POST …/robots/:id/pairing-code → 201 { code, expires_at, installer }; POST …/robots/:id/operators → 201 { operators }; DELETE …/robots/:id/operators/:subject → { operators }; POST …/devices/:id/rotate and /revoke → bot.device-connect-result@1.",
"anyOf": [
{
"type": "object",
@@@ -62878,99 +62971,6 @@
},
{
"$ref": "device-connect-result.v1.json"
-- },
-- {
-- "type": "object",
-- "required": [
-- "audit",
-- "next_before"
-- ],
-- "additionalProperties": false,
-- "properties": {
-- "next_before": {
-- "type": [
-- "integer",
-- "null"
-- ]
-- },
-- "audit": {
-- "type": "array",
-- "items": {
-- "type": "object",
-- "required": [
-- "id",
-- "robot_id",
-- "kind",
-- "result",
-- "at"
-- ],
-- "properties": {
-- "id": {
-- "type": "integer"
-- },
-- "robot_id": {
-- "type": "string",
-- "pattern": "^rob_[0-9A-HJKMNP-TV-Z]{26}$"
-- },
-- "device_id": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "operator_subject": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "operator_kind": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "role": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "kind": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "value": {},
-- "result": {
-- "enum": [
-- "ack",
-- "nack",
-- "refused",
-- "expired"
-- ]
-- },
-- "reason": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "latency_ms": {
-- "type": [
-- "integer",
-- "null"
-- ]
-- },
-- "at": {
-- "type": "string",
-- "format": "date-time"
-- }
-- }
-- }
-- }
-- }
}
]
}
ddiff --git a/generated/openapi/bot.json b/generated/openapi/bot.json
iindex b04348de..c9e28364 100644
---- a/generated/openapi/bot.json
++++ b/generated/openapi/bot.json
@@@ -20,7 +20,7 @@
"post": {
"operationId": "post_api_v1_devices_id_revoke",
"summary": "Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it",
-- "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, read its command audit (newest first), clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
++ "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
"tags": [
"bot.robot"
],
@@@ -86,7 +86,7 @@
"post": {
"operationId": "post_api_v1_devices_id_rotate",
"summary": "Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it",
-- "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, read its command audit (newest first), clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
++ "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
"tags": [
"bot.robot"
],
@@@ -152,7 +152,7 @@
"get": {
"operationId": "get_api_v1_robots",
"summary": "Read robots with a Network service token.",
-- "description": "**bot.robot.read** (first-party): Read robots with a Network service token. GET /robots needs ?owner= and lists that person's robots; the robot routes answer for any robot id with role \"service\": Bot does not check X-OV-Subject or any person's membership for a service token (a person's own token is held to owner, operator or viewer). Devices never carry a credential or key hash. The command audit is not here: Bot gates it with bot.robot.manage. Bot also requires this capability, beside bot.robot.control, to latch the e-stop.",
++ "description": "**bot.robot.read** (first-party): Read robots with a Network service token. GET /robots needs ?owner= and lists that person's robots; the robot routes answer for any robot id with role \"service\": Bot does not check X-OV-Subject or any person's membership for a service token (a person's own token is held to owner, operator or viewer). Devices never carry a credential or key hash. The command audit (GET /robots/:id/audit, newest first, paged) is read too, for any robot id. Bot also requires this capability, beside bot.robot.control, to latch the e-stop.",
"tags": [
"bot.robot"
],
@@@ -196,7 +196,7 @@
"post": {
"operationId": "post_api_v1_robots",
"summary": "Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it",
-- "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, read its command audit (newest first), clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
++ "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
"tags": [
"bot.robot"
],
@@@ -252,7 +252,7 @@
"delete": {
"operationId": "delete_api_v1_robots_id",
"summary": "Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it",
-- "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, read its command audit (newest first), clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
++ "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
"tags": [
"bot.robot"
],
@@@ -309,7 +309,7 @@
"get": {
"operationId": "get_api_v1_robots_id",
"summary": "Read robots with a Network service token.",
-- "description": "**bot.robot.read** (first-party): Read robots with a Network service token. GET /robots needs ?owner= and lists that person's robots; the robot routes answer for any robot id with role \"service\": Bot does not check X-OV-Subject or any person's membership for a service token (a person's own token is held to owner, operator or viewer). Devices never carry a credential or key hash. The command audit is not here: Bot gates it with bot.robot.manage. Bot also requires this capability, beside bot.robot.control, to latch the e-stop.",
++ "description": "**bot.robot.read** (first-party): Read robots with a Network service token. GET /robots needs ?owner= and lists that person's robots; the robot routes answer for any robot id with role \"service\": Bot does not check X-OV-Subject or any person's membership for a service token (a person's own token is held to owner, operator or viewer). Devices never carry a credential or key hash. The command audit (GET /robots/:id/audit, newest first, paged) is read too, for any robot id. Bot also requires this capability, beside bot.robot.control, to latch the e-stop.",
"tags": [
"bot.robot"
],
@@@ -363,7 +363,7 @@
"patch": {
"operationId": "patch_api_v1_robots_id",
"summary": "Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it",
-- "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, read its command audit (newest first), clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
++ "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
"tags": [
"bot.robot"
],
@@@ -428,28 +428,25 @@
"/api/v1/robots/{id}/audit": {
"get": {
"operationId": "get_api_v1_robots_id_audit",
-- "summary": "Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it",
-- "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, read its command audit (newest first), clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
++ "summary": "Read robots with a Network service token.",
++ "description": "**bot.robot.read** (first-party): Read robots with a Network service token. GET /robots needs ?owner= and lists that person's robots; the robot routes answer for any robot id with role \"service\": Bot does not check X-OV-Subject or any person's membership for a service token (a person's own token is held to owner, operator or viewer). Devices never carry a credential or key hash. The command audit (GET /robots/:id/audit, newest first, paged) is read too, for any robot id. Bot also requires this capability, beside bot.robot.control, to latch the e-stop.",
"tags": [
"bot.robot"
],
"x-openvibe-capabilities": [
-- "bot.robot.manage"
++ "bot.robot.read"
],
"x-openvibe-visibility": [
"first-party"
],
-- "x-openvibe-quota-class": "bot-manage",
++ "x-openvibe-quota-class": "bot-read",
"security": [
{
"openvibeToken": [
-- "bot.robot.manage"
++ "bot.robot.read"
]
}
],
-- "x-openvibe-input": {
-- "$ref": "#/components/schemas/bot.robot-manage-request.v1"
-- },
"parameters": [
{
"name": "id",
@@@ -466,7 +463,7 @@
"content": {
"application/json": {
"schema": {
-- "$ref": "#/components/schemas/bot.robot-manage-result.v1"
++ "$ref": "#/components/schemas/bot.robot-read-result.v1"
}
}
}
@@@ -488,7 +485,7 @@
"get": {
"operationId": "get_api_v1_robots_id_devices",
"summary": "Read robots with a Network service token.",
-- "description": "**bot.robot.read** (first-party): Read robots with a Network service token. GET /robots needs ?owner= and lists that person's robots; the robot routes answer for any robot id with role \"service\": Bot does not check X-OV-Subject or any person's membership for a service token (a person's own token is held to owner, operator or viewer). Devices never carry a credential or key hash. The command audit is not here: Bot gates it with bot.robot.manage. Bot also requires this capability, beside bot.robot.control, to latch the e-stop.",
++ "description": "**bot.robot.read** (first-party): Read robots with a Network service token. GET /robots needs ?owner= and lists that person's robots; the robot routes answer for any robot id with role \"service\": Bot does not check X-OV-Subject or any person's membership for a service token (a person's own token is held to owner, operator or viewer). Devices never carry a credential or key hash. The command audit (GET /robots/:id/audit, newest first, paged) is read too, for any robot id. Bot also requires this capability, beside bot.robot.control, to latch the e-stop.",
"tags": [
"bot.robot"
],
@@@ -600,7 +597,7 @@
"post": {
"operationId": "post_api_v1_robots_id_estop_clear",
"summary": "Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it",
-- "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, read its command audit (newest first), clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
++ "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
"tags": [
"bot.robot"
],
@@@ -666,7 +663,7 @@
"get": {
"operationId": "get_api_v1_robots_id_operators",
"summary": "Read robots with a Network service token.",
-- "description": "**bot.robot.read** (first-party): Read robots with a Network service token. GET /robots needs ?owner= and lists that person's robots; the robot routes answer for any robot id with role \"service\": Bot does not check X-OV-Subject or any person's membership for a service token (a person's own token is held to owner, operator or viewer). Devices never carry a credential or key hash. The command audit is not here: Bot gates it with bot.robot.manage. Bot also requires this capability, beside bot.robot.control, to latch the e-stop.",
++ "description": "**bot.robot.read** (first-party): Read robots with a Network service token. GET /robots needs ?owner= and lists that person's robots; the robot routes answer for any robot id with role \"service\": Bot does not check X-OV-Subject or any person's membership for a service token (a person's own token is held to owner, operator or viewer). Devices never carry a credential or key hash. The command audit (GET /robots/:id/audit, newest first, paged) is read too, for any robot id. Bot also requires this capability, beside bot.robot.control, to latch the e-stop.",
"tags": [
"bot.robot"
],
@@@ -720,7 +717,7 @@
"post": {
"operationId": "post_api_v1_robots_id_operators",
"summary": "Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it",
-- "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, read its command audit (newest first), clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
++ "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
"tags": [
"bot.robot"
],
@@@ -786,7 +783,7 @@
"delete": {
"operationId": "delete_api_v1_robots_id_operators_subject",
"summary": "Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it",
-- "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, read its command audit (newest first), clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
++ "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
"tags": [
"bot.robot"
],
@@@ -853,7 +850,7 @@
"post": {
"operationId": "post_api_v1_robots_id_pairing_code",
"summary": "Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it",
-- "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, read its command audit (newest first), clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
++ "description": "**bot.robot.manage** (first-party): Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
"tags": [
"bot.robot"
],
@@@ -1121,7 +1118,7 @@
},
"bot.robot-manage-result.v1": {
"title": "BotRobotManageResult",
-- "description": "bot.robot-manage-result@1: answers of bot.robot.manage on OpenVibe.Bot. POST /api/v1/robots → 201 { robot, pairing }; PATCH …/robots/:id and POST …/robots/:id/estop/clear → { robot }; DELETE …/robots/:id → 204 (no body); POST …/robots/:id/pairing-code → 201 { code, expires_at, installer }; POST …/robots/:id/operators → 201 { operators }; DELETE …/robots/:id/operators/:subject → { operators }; GET …/robots/:id/audit?limit=&before= → { audit, next_before } (newest first); POST …/devices/:id/rotate and /revoke → bot.device-connect-result@1.",
++ "description": "bot.robot-manage-result@1: answers of bot.robot.manage on OpenVibe.Bot. POST /api/v1/robots → 201 { robot, pairing }; PATCH …/robots/:id and POST …/robots/:id/estop/clear → { robot }; DELETE …/robots/:id → 204 (no body); POST …/robots/:id/pairing-code → 201 { code, expires_at, installer }; POST …/robots/:id/operators → 201 { operators }; DELETE …/robots/:id/operators/:subject → { operators }; POST …/devices/:id/rotate and /revoke → bot.device-connect-result@1.",
"anyOf": [
{
"type": "object",
@@@ -1237,105 +1234,12 @@
},
{
"$ref": "#/components/schemas/bot.device-connect-result.v1"
-- },
-- {
-- "type": "object",
-- "required": [
-- "audit",
-- "next_before"
-- ],
-- "additionalProperties": false,
-- "properties": {
-- "next_before": {
-- "type": [
-- "integer",
-- "null"
-- ]
-- },
-- "audit": {
-- "type": "array",
-- "items": {
-- "type": "object",
-- "required": [
-- "id",
-- "robot_id",
-- "kind",
-- "result",
-- "at"
-- ],
-- "properties": {
-- "id": {
-- "type": "integer"
-- },
-- "robot_id": {
-- "type": "string",
-- "pattern": "^rob_[0-9A-HJKMNP-TV-Z]{26}$"
-- },
-- "device_id": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "operator_subject": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "operator_kind": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "role": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "kind": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "value": {},
-- "result": {
-- "enum": [
-- "ack",
-- "nack",
-- "refused",
-- "expired"
-- ]
-- },
-- "reason": {
-- "type": [
-- "string",
-- "null"
-- ]
-- },
-- "latency_ms": {
-- "type": [
-- "integer",
-- "null"
-- ]
-- },
-- "at": {
-- "type": "string",
-- "format": "date-time"
-- }
-- }
-- }
-- }
-- }
}
]
},
"bot.robot-read-result.v1": {
"title": "BotRobotReadResult",
-- "description": "bot.robot-read-result@1: answers of bot.robot.read on OpenVibe.Bot. GET /api/v1/robots?owner= → { robots }; GET …/robots/:id → { robot, role } (role \"service\" for a service token); GET …/robots/:id/operators → { operators }; GET …/robots/:id/devices → { devices } (no hashes, with online).",
++ "description": "bot.robot-read-result@1: answers of bot.robot.read on OpenVibe.Bot (a service token needs no acting subject: only GET …/robots needs ?owner=). GET /api/v1/robots?owner= → { robots }; GET …/robots/:id → { robot, role }; GET …/robots/:id/operators → { operators }; GET …/robots/:id/devices → { devices } (no hashes, with online); GET …/robots/:id/audit?limit=&before= → { audit, next_before } (newest first).",
"anyOf": [
{
"type": "object",
@@@ -1425,6 +1329,99 @@
}
}
}
++ },
++ {
++ "type": "object",
++ "required": [
++ "audit",
++ "next_before"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "next_before": {
++ "type": [
++ "integer",
++ "null"
++ ]
++ },
++ "audit": {
++ "type": "array",
++ "items": {
++ "type": "object",
++ "required": [
++ "id",
++ "robot_id",
++ "kind",
++ "result",
++ "at"
++ ],
++ "properties": {
++ "id": {
++ "type": "integer"
++ },
++ "robot_id": {
++ "type": "string",
++ "pattern": "^rob_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "device_id": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "operator_subject": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "operator_kind": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "role": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "kind": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "value": {},
++ "result": {
++ "enum": [
++ "ack",
++ "nack",
++ "refused",
++ "expired"
++ ]
++ },
++ "reason": {
++ "type": [
++ "string",
++ "null"
++ ]
++ },
++ "latency_ms": {
++ "type": [
++ "integer",
++ "null"
++ ]
++ },
++ "at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++ }
++ }
++ }
}
]
},
ddiff --git a/generated/typescript/index.d.ts b/generated/typescript/index.d.ts
iindex 5c2175d4..235ee084 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -52231,7 +52231,7 @@ export interface BotDevice {
/** [email protected] (owner: bot) */
/**
-- * bot.robot-read-result@1: answers of bot.robot.read on OpenVibe.Bot. GET /api/v1/robots?owner= → { robots }; GET …/robots/:id → { robot, role } (role "service" for a service token); GET …/robots/:id/operators → { operators }; GET …/robots/:id/devices → { devices } (no hashes, with online).
++ * bot.robot-read-result@1: answers of bot.robot.read on OpenVibe.Bot (a service token needs no acting subject: only GET …/robots needs ?owner=). GET /api/v1/robots?owner= → { robots }; GET …/robots/:id → { robot, role }; GET …/robots/:id/operators → { operators }; GET …/robots/:id/devices → { devices } (no hashes, with online); GET …/robots/:id/audit?limit=&before= → { audit, next_before } (newest first).
*/
export type BotRobotReadResult =
| {
@@@ -52251,6 +52251,23 @@ export type BotRobotReadResult =
}
| {
devices: BotDevice[];
++ }
++ | {
++ next_before: number | null;
++ audit: {
++ id: number;
++ robot_id: string;
++ device_id?: string | null;
++ operator_subject?: string | null;
++ operator_kind?: string | null;
++ role?: string | null;
++ kind: string | null;
++ value?: unknown;
++ result: "ack" | "nack" | "refused" | "expired";
++ reason?: string | null;
++ latency_ms?: number | null;
++ at: string;
++ }[];
};
/** [email protected] (owner: bot) */
@@@ -52277,7 +52294,7 @@ export type BotRobotManageRequest =
/** [email protected] (owner: bot) */
/**
-- * bot.robot-manage-result@1: answers of bot.robot.manage on OpenVibe.Bot. POST /api/v1/robots → 201 { robot, pairing }; PATCH …/robots/:id and POST …/robots/:id/estop/clear → { robot }; DELETE …/robots/:id → 204 (no body); POST …/robots/:id/pairing-code → 201 { code, expires_at, installer }; POST …/robots/:id/operators → 201 { operators }; DELETE …/robots/:id/operators/:subject → { operators }; GET …/robots/:id/audit?limit=&before= → { audit, next_before } (newest first); POST …/devices/:id/rotate and /revoke → bot.device-connect-result@1.
++ * bot.robot-manage-result@1: answers of bot.robot.manage on OpenVibe.Bot. POST /api/v1/robots → 201 { robot, pairing }; PATCH …/robots/:id and POST …/robots/:id/estop/clear → { robot }; DELETE …/robots/:id → 204 (no body); POST …/robots/:id/pairing-code → 201 { code, expires_at, installer }; POST …/robots/:id/operators → 201 { operators }; DELETE …/robots/:id/operators/:subject → { operators }; POST …/devices/:id/rotate and /revoke → bot.device-connect-result@1.
*/
export type BotRobotManageResult =
| {
@@@ -52307,24 +52324,7 @@ export type BotRobotManageResult =
created_at?: string;
}[];
}
-- | BotDeviceConnectResult
-- | {
-- next_before: number | null;
-- audit: {
-- id: number;
-- robot_id: string;
-- device_id?: string | null;
-- operator_subject?: string | null;
-- operator_kind?: string | null;
-- role?: string | null;
-- kind: string | null;
-- value?: unknown;
-- result: "ack" | "nack" | "refused" | "expired";
-- reason?: string | null;
-- latency_ms?: number | null;
-- at: string;
-- }[];
-- };
++ | BotDeviceConnectResult;
/** [email protected] (owner: bot) */
/**
* bot.robot-control-result@1: answers of bot.robot.control on OpenVibe.Bot. POST /api/v1/robots/:id/estop → { robot } with its e-stop as it now stands.
ddiff --git a/manifests/capabilities/bot.robot.manage.json b/manifests/capabilities/bot.robot.manage.json
iindex af017c5a..01088769 100644
---- a/manifests/capabilities/bot.robot.manage.json
++++ b/manifests/capabilities/bot.robot.manage.json
@@@ -5,7 +5,7 @@
"status": "active",
"visibility": "first-party",
"sensitive": true,
-- "description": "Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, read its command audit (newest first), clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
++ "description": "Manage robots with a Network service token: create one for the person the body's owner or X-OV-Subject names (a new robot and its pairing code), and, for any robot id, rename, reconfigure or delete it (irreversible), issue a one-time pairing code, add or remove operators, clear its e-stop, and rotate a paired device's credentials (the new credential and publish key are shown once) or revoke the device, which closes its socket at once. Bot does not check the acting person against the robot's owner for a service token (a person's own token must be the owner's).",
"permissions": [
"bot:robot:manage"
],
@@@ -23,7 +23,6 @@
"POST /api/v1/robots/:id/pairing-code",
"POST /api/v1/robots/:id/operators",
"DELETE /api/v1/robots/:id/operators/:subject",
-- "GET /api/v1/robots/:id/audit",
"POST /api/v1/robots/:id/estop/clear",
"POST /api/v1/devices/:id/rotate",
"POST /api/v1/devices/:id/revoke"
ddiff --git a/manifests/capabilities/bot.robot.read.json b/manifests/capabilities/bot.robot.read.json
iindex 1560006e..504fa8eb 100644
---- a/manifests/capabilities/bot.robot.read.json
++++ b/manifests/capabilities/bot.robot.read.json
@@@ -4,7 +4,7 @@
"owner": "bot",
"status": "active",
"visibility": "first-party",
-- "description": "Read robots with a Network service token. GET /robots needs ?owner= and lists that person's robots; the robot routes answer for any robot id with role \"service\": Bot does not check X-OV-Subject or any person's membership for a service token (a person's own token is held to owner, operator or viewer). Devices never carry a credential or key hash. The command audit is not here: Bot gates it with bot.robot.manage. Bot also requires this capability, beside bot.robot.control, to latch the e-stop.",
++ "description": "Read robots with a Network service token. GET /robots needs ?owner= and lists that person's robots; the robot routes answer for any robot id with role \"service\": Bot does not check X-OV-Subject or any person's membership for a service token (a person's own token is held to owner, operator or viewer). Devices never carry a credential or key hash. The command audit (GET /robots/:id/audit, newest first, paged) is read too, for any robot id. Bot also requires this capability, beside bot.robot.control, to latch the e-stop.",
"permissions": [
"bot:robot:read"
],
@@@ -17,7 +17,8 @@
"GET /api/v1/robots",
"GET /api/v1/robots/:id",
"GET /api/v1/robots/:id/operators",
-- "GET /api/v1/robots/:id/devices"
++ "GET /api/v1/robots/:id/devices",
++ "GET /api/v1/robots/:id/audit"
],
"inputSchema": "common.no-body@1",
"outputSchema": "bot.robot-read-result@1"