openvibers/openvibe.contracts Commits
Add agent token claims and confirmation lifecycle contracts (#23)
Changes
@@@ -4,6 +4,35 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) thfrom `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,the notes were in the tag and commit messages (`git tag -n1`).++## 0.89.0 — 2026-10-03++++**Agent tokens and confirmations** (plan T2 step 9; OpenVibe.Network `docs/t2-projects-and-grants.md` §2 gaps 1–3,++§4 and §5). These are the contracts that Network WS-Z2 slices 7–9 wait on. All of it is additive.++- `identity.service-token-claims` 1.3.0: `sub` also accepts `agent:agt_<ULID>` (`ids.principalSub({ type: 'agent' })`)++ and `actor_type` gains `agent`. An agent token must carry `project_id`, `env` and `on_behalf_of` (the owner, `usr_`).++ It may carry `cap_confirm` (capabilities held in confirm mode: each use needs an approved++ `network.confirmation-request@1`, and they never appear in `cap`, so a receiver that does not know agents refuses++ them) and `act` (RFC 8693 actor claim: `{ sub: 'svc:<slug>' | 'app:app_<ULID>' }`, the host, nothing else).++ `if`/`then` rules tie an `agent:` sub to `actor_type: agent` in both directions. Service, app, mod and node++ tokens validate as before.++ Fixtures: valid `agent-service-host` and `agent-app-host-auto-only`; invalid `agent-without-owner`,++ `agent-sub-as-service`, `agent-actor-with-service-sub` and `agent-bad-actor-claim`.++- New capability `network.confirmation.manage` (planned, first-party, listed by `manifests/services/network.json`). It is++ the owning service's side of a confirmation: create one (`POST /internal/confirmations`), read it, consume it once++ with the action's digest, or cancel it. The owner's inbox (`/api/v1/confirmations`: list, approve, deny) runs on the++ owner's own session, and no token holds it. The request and response bodies are Network-local until the routes are++ built.++- New event `network.confirmation.changed@1` (planned, produced by Network), the shape of Network's proposal++ (`docs/contracts-proposal/network.confirmation.changed.v1.json`). Payload: `confirmation_id` (`cnf_`), `agent_id`++ (`agt_`), `project_id`, `capability`, `audience` (the owning service), `state` (`pending | approved | denied | expired++ | cancelled`, the names of `network.confirmation-request@1`) and `change` (`created | approved | denied | expired |++ cancelled | used`), and optional `standing_rule`, `rule_id`, `cancel_reason`, `expires_at` and `changed_at`. `created`++ leaves the state pending, or approved when a standing rule covers it; `used` leaves it approved; every other change++ names its state. `cancel_reason` is required on cancelled and absent otherwise. The event never carries `summary` or++ `details`. Fixtures: valid `requested`, `approved-by-owner`, `approved-by-standing-rule`, `used`, `cancelled` and++ `expired`; invalid `requested-state`, `cancelled-without-reason`, `used-not-approved`, `missing-audience` and++ `with-summary`.++## 0.88.0 — 2026-10-03**The Fabric ADR and the user-owned trust rule** (plan T1 step 3; T14 S2, the Contracts half). ADR-046 "The universal@@@ -23,7 +23,7 @@},{"id": "identity.service-token-claims",-- "version": "1.2.0",++ "version": "1.3.0","owner": "network","schema": "identity/service-token-claims.v1.json","visibility": "public",@@@ -5552,6 +5552,16 @@"status": "active","adr": "ADR-034"},++ {++ "id": "network.confirmation.changed",++ "version": "1.0.0",++ "owner": "network",++ "schema": "events/payloads/network.confirmation.changed.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "planned",++ "adr": "ADR-034"++ },{"id": "zone.object-zone","version": "1.0.0",nnew file mode 100644@@@ -0,0 +1,246 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/network.confirmation.changed.v1.json",++ "title": "NetworkConfirmationChangedPayload",++ "description": "network.confirmation.changed v1 (OpenVibe.Network; roadmap WS-Z2, docs/t2-projects-and-grants.md section 5; shape from OpenVibe.Network docs/contracts-proposal/network.confirmation.changed.v1.json). A confirmation request (network.confirmation-request@1) an agent raised for a sensitive capability was created, approved, denied, expired, cancelled or used. state is the state after the change, named as in network.confirmation-request@1: created leaves it pending (or approved when a standing rule covers it, with rule_id), used leaves it approved, and every other change names its state. A cancelled change says why in cancel_reason. Written to Network's outbox in the transaction that changes it, beside an audit row. Never carries the summary or details, which may hold message text: a reader that needs them asks Network with its own authority. Envelope: subject { type: confirmation, id: cnf_… }, visibility internal, on_behalf_of the owner, actor the owner who decided (or the system for an expiry, a cascade or a standing rule).",++ "type": "object",++ "required": [++ "confirmation_id",++ "agent_id",++ "project_id",++ "capability",++ "audience",++ "state",++ "change"++ ],++ "additionalProperties": false,++ "properties": {++ "confirmation_id": {++ "type": "string",++ "pattern": "^cnf_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "agent_id": {++ "type": "string",++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The agent that asked."++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The agent's developer project."++ },++ "capability": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"++ },++ "audience": {++ "type": "string",++ "pattern": "^openvibe\\.[a-z][a-z0-9-]{0,63}$",++ "description": "The owning service of the capability: the only one that may consume or cancel the confirmation."++ },++ "state": {++ "type": "string",++ "enum": [++ "pending",++ "approved",++ "denied",++ "expired",++ "cancelled"++ ],++ "description": "The state after the change. A use leaves it approved and sets used_at."++ },++ "change": {++ "type": "string",++ "enum": [++ "created",++ "approved",++ "denied",++ "expired",++ "cancelled",++ "used"++ ]++ },++ "standing_rule": {++ "type": "string",++ "enum": [++ "once",++ "session",++ "until",++ "always"++ ],++ "description": "On approved: the rule the owner chose (once creates none)."++ },++ "rule_id": {++ "type": "integer",++ "minimum": 1,++ "description": "On created through a standing rule, or on approved with a session, until or always rule: that rule."++ },++ "cancel_reason": {++ "type": "string",++ "enum": [++ "service",++ "agent_paused",++ "agent_revoked",++ "grant_revoked",++ "grant_expired",++ "beyond_host",++ "member_removed",++ "account_erased",++ "app_revoked",++ "project_archived"++ ],++ "description": "On cancelled: what took the authority away."++ },++ "expires_at": {++ "type": "string",++ "format": "date-time"++ },++ "changed_at": {++ "type": "string",++ "format": "date-time"++ }++ },++ "allOf": [++ {++ "if": {++ "properties": {++ "change": {++ "const": "created"++ }++ },++ "required": [++ "change"++ ]++ },++ "then": {++ "properties": {++ "state": {++ "enum": [++ "pending",++ "approved"++ ]++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "change": {++ "const": "used"++ }++ },++ "required": [++ "change"++ ]++ },++ "then": {++ "properties": {++ "state": {++ "const": "approved"++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "change": {++ "const": "approved"++ }++ },++ "required": [++ "change"++ ]++ },++ "then": {++ "properties": {++ "state": {++ "const": "approved"++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "change": {++ "const": "denied"++ }++ },++ "required": [++ "change"++ ]++ },++ "then": {++ "properties": {++ "state": {++ "const": "denied"++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "change": {++ "const": "expired"++ }++ },++ "required": [++ "change"++ ]++ },++ "then": {++ "properties": {++ "state": {++ "const": "expired"++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "change": {++ "const": "cancelled"++ }++ },++ "required": [++ "change"++ ]++ },++ "then": {++ "properties": {++ "state": {++ "const": "cancelled"++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "state": {++ "const": "cancelled"++ }++ },++ "required": [++ "state"++ ]++ },++ "then": {++ "required": [++ "cancel_reason"++ ]++ },++ "else": {++ "not": {++ "required": [++ "cancel_reason"++ ]++ }++ }++ }++ ]++}@@@ -2,7 +2,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/identity/service-token-claims.v1.json","title": "ServiceTokenClaims",-- "description": "Claims of a short-lived RS256 client-credentials token issued by OpenVibe.Network to a service, app, mod or node principal. Replaces X-Internal-Key. App tokens (actor_type app) also carry project_id and env; receivers refuse env=sandbox unless they opted in.",++ "description": "Claims of a short-lived RS256 client-credentials token issued by OpenVibe.Network to a service, app, mod, node or agent principal. Replaces X-Internal-Key. App tokens (actor_type app) also carry project_id and env; receivers refuse env=sandbox unless they opted in. Agent tokens (actor_type agent, sub agent:agt_<ULID>) are minted for the agent's host and also carry project_id, env and on_behalf_of (the owner), and optionally cap_confirm and act.","type": "object","required": ["iss",@@@ -23,14 +23,15 @@},"sub": {"type": "string",-- "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|node:nod_[0-9A-HJKMNP-TV-Z]{26})$"++ "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|node:nod_[0-9A-HJKMNP-TV-Z]{26}|agent:agt_[0-9A-HJKMNP-TV-Z]{26})$"},"actor_type": {"enum": ["service","app","mod",-- "node"++ "node",++ "agent"]},"aud": {@@@ -49,6 +50,14 @@},"description": "Granted capability ids; a trailing .* grants a family."},++ "cap_confirm": {++ "type": "array",++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"++ },++ "description": "Agent tokens: capabilities the agent holds in confirm mode. Each use needs an approved network.confirmation-request@1, presented as OpenVibe-Confirmation: cnf_<ULID> and consumed by the owning service. Never also in cap, so a receiver that does not know agents refuses those actions."++ },"ns": {"type": "array","items": {@@@ -69,19 +78,33 @@"project_id": {"type": "string","pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "Developer project of an app principal (ADR-014). Services key tenancy by it. Absent on first-party service tokens."++ "description": "Developer project of an app or agent principal (ADR-014). Services key tenancy by it. Absent on first-party service tokens."},"env": {"enum": ["sandbox","production"],-- "description": "Environment of an app principal. A receiver MUST refuse env=sandbox (401 token.sandbox_refused) unless it opted in to sandbox tokens. Absent on first-party service tokens, which are production."++ "description": "Environment of an app or agent principal. A receiver MUST refuse env=sandbox (401 token.sandbox_refused) unless it opted in to sandbox tokens. Absent on first-party service tokens, which are production."},"on_behalf_of": {"type": "string","pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "The person who authorized an app through the authorization-code flow. Absent on client_credentials tokens."++ "description": "The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Absent on client_credentials tokens."++ },++ "act": {++ "type": "object",++ "required": [++ "sub"++ ],++ "additionalProperties": false,++ "properties": {++ "sub": {++ "type": "string",++ "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26})$"++ }++ },++ "description": "Agent tokens: the host that runs the agent and the token was minted for (RFC 8693 actor claim), a service or an app."}},"allOf": [@@@ -102,6 +125,54 @@"env"]}++ },++ {++ "if": {++ "properties": {++ "actor_type": {++ "const": "agent"++ }++ },++ "required": [++ "actor_type"++ ]++ },++ "then": {++ "required": [++ "project_id",++ "env",++ "on_behalf_of"++ ],++ "properties": {++ "sub": {++ "type": "string",++ "pattern": "^agent:agt_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "sub": {++ "type": "string",++ "pattern": "^agent:"++ }++ },++ "required": [++ "sub"++ ]++ },++ "then": {++ "required": [++ "actor_type"++ ],++ "properties": {++ "actor_type": {++ "const": "agent"++ }++ }++ }}]}nnew file mode 100644@@@ -0,0 +1,17 @@++{++ "iss": "https://openvibe.network",++ "sub": "svc:actor",++ "actor_type": "agent",++ "aud": [++ "openvibe.chat"++ ],++ "cap": [++ "chat.presence.read"++ ],++ "iat": 1790000000,++ "exp": 1790000300,++ "jti": "tok_agent005",++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "env": "production",++ "on_behalf_of": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++}nnew file mode 100644@@@ -0,0 +1,20 @@++{++ "iss": "https://openvibe.network",++ "sub": "agent:agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "actor_type": "agent",++ "aud": [++ "openvibe.chat"++ ],++ "cap": [++ "chat.presence.read"++ ],++ "iat": 1790000000,++ "exp": 1790000300,++ "jti": "tok_agent006",++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "env": "production",++ "on_behalf_of": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "act": {++ "sub": "agent:agt_01J0000000000000000000000Z"++ }++}nnew file mode 100644@@@ -0,0 +1,14 @@++{++ "iss": "https://openvibe.network",++ "sub": "agent:agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "actor_type": "service",++ "aud": [++ "openvibe.chat"++ ],++ "cap": [++ "chat.message.send"++ ],++ "iat": 1790000000,++ "exp": 1790000300,++ "jti": "tok_agent004"++}nnew file mode 100644@@@ -0,0 +1,20 @@++{++ "iss": "https://openvibe.network",++ "sub": "agent:agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "actor_type": "agent",++ "aud": [++ "openvibe.chat"++ ],++ "cap": [],++ "cap_confirm": [++ "chat.message.send"++ ],++ "iat": 1790000000,++ "exp": 1790000300,++ "jti": "tok_agent003",++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "env": "production",++ "act": {++ "sub": "svc:actor"++ }++}nnew file mode 100644@@@ -0,0 +1,24 @@++{++ "iss": "https://openvibe.network",++ "sub": "agent:agt_01J0000000000000000000000Z",++ "actor_type": "agent",++ "aud": [++ "openvibe.media"++ ],++ "cap": [++ "media.object.read"++ ],++ "ns": [++ "prj_01J0000000000000000000000Z",++ "app.prj_01J0000000000000000000000Z.*"++ ],++ "iat": 1790000000,++ "exp": 1790000300,++ "jti": "tok_agent002",++ "project_id": "prj_01J0000000000000000000000Z",++ "env": "sandbox",++ "on_behalf_of": "usr_01J0000000000000000000000Z",++ "act": {++ "sub": "app:app_01J0000000000000000000000Z"++ }++}nnew file mode 100644@@@ -0,0 +1,26 @@++{++ "iss": "https://openvibe.network",++ "sub": "agent:agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "actor_type": "agent",++ "aud": [++ "openvibe.chat"++ ],++ "cap": [++ "chat.presence.read"++ ],++ "cap_confirm": [++ "chat.message.send"++ ],++ "ns": [++ "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ ],++ "iat": 1790000000,++ "exp": 1790000300,++ "jti": "tok_agent001",++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "env": "production",++ "on_behalf_of": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "act": {++ "sub": "svc:actor"++ }++}nnew file mode 100644@@@ -0,0 +1 @@++{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"cancelled","change":"cancelled"}nnew file mode 100644@@@ -0,0 +1 @@++{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","state":"pending","change":"created"}nnew file mode 100644@@@ -0,0 +1 @@++{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"requested","change":"created"}nnew file mode 100644@@@ -0,0 +1 @@++{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"pending","change":"used"}nnew file mode 100644@@@ -0,0 +1 @@++{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"pending","change":"created","summary":"Send 'see you at 8' to Sam"}nnew file mode 100644@@@ -0,0 +1 @@++{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"approved","change":"approved","standing_rule":"session","rule_id":7,"expires_at":"2026-10-03T12:15:00Z","changed_at":"2026-10-03T12:02:00Z"}nnew file mode 100644@@@ -0,0 +1 @@++{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"approved","change":"created","rule_id":7,"changed_at":"2026-10-03T12:05:00Z"}nnew file mode 100644@@@ -0,0 +1 @@++{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"cancelled","change":"cancelled","cancel_reason":"agent_revoked","changed_at":"2026-10-03T12:07:00Z"}nnew file mode 100644@@@ -0,0 +1 @@++{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"space.post.write","audience":"openvibe.space","state":"expired","change":"expired","changed_at":"2026-10-03T12:15:00Z"}nnew file mode 100644@@@ -0,0 +1 @@++{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"pending","change":"created","expires_at":"2026-10-03T12:15:00Z","changed_at":"2026-10-03T12:00:00Z"}nnew file mode 100644@@@ -0,0 +1 @@++{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"network.integration.github.read","audience":"openvibe.network","state":"approved","change":"used","changed_at":"2026-10-03T12:06:00Z"}@@@ -180,14 +180,14 @@}},"identity.service-token-claims": {-- "version": "1.2.0",++ "version": "1.3.0","owner": "network","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/identity/service-token-claims.v1.json","title": "ServiceTokenClaims",-- "description": "Claims of a short-lived RS256 client-credentials token issued by OpenVibe.Network to a service, app, mod or node principal. Replaces X-Internal-Key. App tokens (actor_type app) also carry project_id and env; receivers refuse env=sandbox unless they opted in.",++ "description": "Claims of a short-lived RS256 client-credentials token issued by OpenVibe.Network to a service, app, mod, node or agent principal. Replaces X-Internal-Key. App tokens (actor_type app) also carry project_id and env; receivers refuse env=sandbox unless they opted in. Agent tokens (actor_type agent, sub agent:agt_<ULID>) are minted for the agent's host and also carry project_id, env and on_behalf_of (the owner), and optionally cap_confirm and act.","type": "object","required": ["iss",@@@ -208,14 +208,15 @@},"sub": {"type": "string",-- "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|node:nod_[0-9A-HJKMNP-TV-Z]{26})$"++ "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|node:nod_[0-9A-HJKMNP-TV-Z]{26}|agent:agt_[0-9A-HJKMNP-TV-Z]{26})$"},"actor_type": {"enum": ["service","app","mod",-- "node"++ "node",++ "agent"]},"aud": {@@@ -234,6 +235,14 @@},"description": "Granted capability ids; a trailing .* grants a family."},++ "cap_confirm": {++ "type": "array",++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"++ },++ "description": "Agent tokens: capabilities the agent holds in confirm mode. Each use needs an approved network.confirmation-request@1, presented as OpenVibe-Confirmation: cnf_<ULID> and consumed by the owning service. Never also in cap, so a receiver that does not know agents refuses those actions."++ },"ns": {"type": "array","items": {@@@ -254,19 +263,33 @@"project_id": {"type": "string","pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "Developer project of an app principal (ADR-014). Services key tenancy by it. Absent on first-party service tokens."++ "description": "Developer project of an app or agent principal (ADR-014). Services key tenancy by it. Absent on first-party service tokens."},"env": {"enum": ["sandbox","production"],-- "description": "Environment of an app principal. A receiver MUST refuse env=sandbox (401 token.sandbox_refused) unless it opted in to sandbox tokens. Absent on first-party service tokens, which are production."++ "description": "Environment of an app or agent principal. A receiver MUST refuse env=sandbox (401 token.sandbox_refused) unless it opted in to sandbox tokens. Absent on first-party service tokens, which are production."},"on_behalf_of": {"type": "string","pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "The person who authorized an app through the authorization-code flow. Absent on client_credentials tokens."++ "description": "The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Absent on client_credentials tokens."++ },++ "act": {++ "type": "object",++ "required": [++ "sub"++ ],++ "additionalProperties": false,++ "properties": {++ "sub": {++ "type": "string",++ "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26})$"++ }++ },++ "description": "Agent tokens: the host that runs the agent and the token was minted for (RFC 8693 actor claim), a service or an app."}},"allOf": [@@@ -287,6 +310,54 @@"env"]}++ },++ {++ "if": {++ "properties": {++ "actor_type": {++ "const": "agent"++ }++ },++ "required": [++ "actor_type"++ ]++ },++ "then": {++ "required": [++ "project_id",++ "env",++ "on_behalf_of"++ ],++ "properties": {++ "sub": {++ "type": "string",++ "pattern": "^agent:agt_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "sub": {++ "type": "string",++ "pattern": "^agent:"++ }++ },++ "required": [++ "sub"++ ]++ },++ "then": {++ "required": [++ "actor_type"++ ],++ "properties": {++ "actor_type": {++ "const": "agent"++ }++ }++ }}]}@@@ -61835,6 +61906,257 @@}}},++ "network.confirmation.changed": {++ "version": "1.0.0",++ "owner": "network",++ "status": "planned",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/network.confirmation.changed.v1.json",++ "title": "NetworkConfirmationChangedPayload",++ "description": "network.confirmation.changed v1 (OpenVibe.Network; roadmap WS-Z2, docs/t2-projects-and-grants.md section 5; shape from OpenVibe.Network docs/contracts-proposal/network.confirmation.changed.v1.json). A confirmation request (network.confirmation-request@1) an agent raised for a sensitive capability was created, approved, denied, expired, cancelled or used. state is the state after the change, named as in network.confirmation-request@1: created leaves it pending (or approved when a standing rule covers it, with rule_id), used leaves it approved, and every other change names its state. A cancelled change says why in cancel_reason. Written to Network's outbox in the transaction that changes it, beside an audit row. Never carries the summary or details, which may hold message text: a reader that needs them asks Network with its own authority. Envelope: subject { type: confirmation, id: cnf_… }, visibility internal, on_behalf_of the owner, actor the owner who decided (or the system for an expiry, a cascade or a standing rule).",++ "type": "object",++ "required": [++ "confirmation_id",++ "agent_id",++ "project_id",++ "capability",++ "audience",++ "state",++ "change"++ ],++ "additionalProperties": false,++ "properties": {++ "confirmation_id": {++ "type": "string",++ "pattern": "^cnf_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "agent_id": {++ "type": "string",++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The agent that asked."++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The agent's developer project."++ },++ "capability": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"++ },++ "audience": {++ "type": "string",++ "pattern": "^openvibe\\.[a-z][a-z0-9-]{0,63}$",++ "description": "The owning service of the capability: the only one that may consume or cancel the confirmation."++ },++ "state": {++ "type": "string",++ "enum": [++ "pending",++ "approved",++ "denied",++ "expired",++ "cancelled"++ ],++ "description": "The state after the change. A use leaves it approved and sets used_at."++ },++ "change": {++ "type": "string",++ "enum": [++ "created",++ "approved",++ "denied",++ "expired",++ "cancelled",++ "used"++ ]++ },++ "standing_rule": {++ "type": "string",++ "enum": [++ "once",++ "session",++ "until",++ "always"++ ],++ "description": "On approved: the rule the owner chose (once creates none)."++ },++ "rule_id": {++ "type": "integer",++ "minimum": 1,++ "description": "On created through a standing rule, or on approved with a session, until or always rule: that rule."++ },++ "cancel_reason": {++ "type": "string",++ "enum": [++ "service",++ "agent_paused",++ "agent_revoked",++ "grant_revoked",++ "grant_expired",++ "beyond_host",++ "member_removed",++ "account_erased",++ "app_revoked",++ "project_archived"++ ],++ "description": "On cancelled: what took the authority away."++ },++ "expires_at": {++ "type": "string",++ "format": "date-time"++ },++ "changed_at": {++ "type": "string",++ "format": "date-time"++ }++ },++ "allOf": [++ {++ "if": {++ "properties": {++ "change": {++ "const": "created"++ }++ },++ "required": [++ "change"++ ]++ },++ "then": {++ "properties": {++ "state": {++ "enum": [++ "pending",++ "approved"++ ]++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "change": {++ "const": "used"++ }++ },++ "required": [++ "change"++ ]++ },++ "then": {++ "properties": {++ "state": {++ "const": "approved"++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "change": {++ "const": "approved"++ }++ },++ "required": [++ "change"++ ]++ },++ "then": {++ "properties": {++ "state": {++ "const": "approved"++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "change": {++ "const": "denied"++ }++ },++ "required": [++ "change"++ ]++ },++ "then": {++ "properties": {++ "state": {++ "const": "denied"++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "change": {++ "const": "expired"++ }++ },++ "required": [++ "change"++ ]++ },++ "then": {++ "properties": {++ "state": {++ "const": "expired"++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "change": {++ "const": "cancelled"++ }++ },++ "required": [++ "change"++ ]++ },++ "then": {++ "properties": {++ "state": {++ "const": "cancelled"++ }++ }++ }++ },++ {++ "if": {++ "properties": {++ "state": {++ "const": "cancelled"++ }++ },++ "required": [++ "state"++ ]++ },++ "then": {++ "required": [++ "cancel_reason"++ ]++ },++ "else": {++ "not": {++ "required": [++ "cancel_reason"++ ]++ }++ }++ }++ ]++ }++ },"zone.object-zone": {"version": "1.0.0","owner": "zone",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.AI API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Billing API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Blog API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bot API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Chat API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Codes API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Community API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Coupons API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Deals API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Events API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Games API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Host API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Live API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Media API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Network API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.News API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenRe.Stream API",-- "version": "0.88.0",-- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Reviews API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Search API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Sources API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Space API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tips API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tools API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Trade API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.VIP API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Wiki API",-- "version": "0.88.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.89.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -51,9 +51,9 @@ export interface LegacyIdentityMapEntry {metadata?: {};}--/** [email protected] (owner: network) */++/** [email protected] (owner: network) *//**-- * Claims of a short-lived RS256 client-credentials token issued by OpenVibe.Network to a service, app, mod or node principal. Replaces X-Internal-Key. App tokens (actor_type app) also carry project_id and env; receivers refuse env=sandbox unless they opted in.++ * Claims of a short-lived RS256 client-credentials token issued by OpenVibe.Network to a service, app, mod, node or agent principal. Replaces X-Internal-Key. App tokens (actor_type app) also carry project_id and env; receivers refuse env=sandbox unless they opted in. Agent tokens (actor_type agent, sub agent:agt_<ULID>) are minted for the agent's host and also carry project_id, env and on_behalf_of (the owner), and optionally cap_confirm and act.*/export type ServiceTokenClaims = {[k: string]: unknown | undefined;@@@ -63,7 +63,7 @@ export type ServiceTokenClaims = {*/iss: string;sub: string;-- actor_type: "service" | "app" | "mod" | "node";++ actor_type: "service" | "app" | "mod" | "node" | "agent";/*** @minItems 1*/@@@ -72,6 +72,10 @@ export type ServiceTokenClaims = {* Granted capability ids; a trailing .* grants a family.*/cap: string[];++ /**++ * Agent tokens: capabilities the agent holds in confirm mode. Each use needs an approved network.confirmation-request@1, presented as OpenVibe-Confirmation: cnf_<ULID> and consumed by the owning service. Never also in cap, so a receiver that does not know agents refuses those actions.++ */++ cap_confirm?: string[];/*** Namespace constraints, e.g. live.* or mod.example.*. A developer app token carries its project_id (the whole project, as issued before namespaces were rows) and app.<project_id>.*: on Media, app.<project_id> and app.<project_id>.sandbox are the project's production and sandbox namespaces, with children below them.*/@@@ -80,17 +84,23 @@ export type ServiceTokenClaims = {exp: number;jti: string;/**-- * Developer project of an app principal (ADR-014). Services key tenancy by it. Absent on first-party service tokens.++ * Developer project of an app or agent principal (ADR-014). Services key tenancy by it. Absent on first-party service tokens.*/project_id?: string;/**-- * Environment of an app principal. A receiver MUST refuse env=sandbox (401 token.sandbox_refused) unless it opted in to sandbox tokens. Absent on first-party service tokens, which are production.++ * Environment of an app or agent principal. A receiver MUST refuse env=sandbox (401 token.sandbox_refused) unless it opted in to sandbox tokens. Absent on first-party service tokens, which are production.*/env?: "sandbox" | "production";/**-- * The person who authorized an app through the authorization-code flow. Absent on client_credentials tokens.++ * The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Absent on client_credentials tokens.*/on_behalf_of?: string;++ /**++ * Agent tokens: the host that runs the agent and the token was minted for (RFC 8693 actor claim), a service or an app.++ */++ act?: {++ sub: string;++ };[k: string]: unknown | undefined;};@@@ -52018,6 +52028,58 @@ export interface ConfirmationRequest {created_at: string;}++/** [email protected] (owner: network) */++/**++ * network.confirmation.changed v1 (OpenVibe.Network; roadmap WS-Z2, docs/t2-projects-and-grants.md section 5; shape from OpenVibe.Network docs/contracts-proposal/network.confirmation.changed.v1.json). A confirmation request (network.confirmation-request@1) an agent raised for a sensitive capability was created, approved, denied, expired, cancelled or used. state is the state after the change, named as in network.confirmation-request@1: created leaves it pending (or approved when a standing rule covers it, with rule_id), used leaves it approved, and every other change names its state. A cancelled change says why in cancel_reason. Written to Network's outbox in the transaction that changes it, beside an audit row. Never carries the summary or details, which may hold message text: a reader that needs them asks Network with its own authority. Envelope: subject { type: confirmation, id: cnf_… }, visibility internal, on_behalf_of the owner, actor the owner who decided (or the system for an expiry, a cascade or a standing rule).++ */++export type NetworkConfirmationChangedPayload = {++ [k: string]: unknown | undefined;++} & {++ confirmation_id: string;++ /**++ * The agent that asked.++ */++ agent_id: string;++ /**++ * The agent's developer project.++ */++ project_id: string;++ capability: string;++ /**++ * The owning service of the capability: the only one that may consume or cancel the confirmation.++ */++ audience: string;++ /**++ * The state after the change. A use leaves it approved and sets used_at.++ */++ state: "pending" | "approved" | "denied" | "expired" | "cancelled";++ change: "created" | "approved" | "denied" | "expired" | "cancelled" | "used";++ /**++ * On approved: the rule the owner chose (once creates none).++ */++ standing_rule?: "once" | "session" | "until" | "always";++ /**++ * On created through a standing rule, or on approved with a session, until or always rule: that rule.++ */++ rule_id?: number;++ /**++ * On cancelled: what took the authority away.++ */++ cancel_reason?:++ | "service"++ | "agent_paused"++ | "agent_revoked"++ | "grant_revoked"++ | "grant_expired"++ | "beyond_host"++ | "member_removed"++ | "account_erased"++ | "app_revoked"++ | "project_archived";++ expires_at?: string;++ changed_at?: string;++};++/** [email protected] (owner: zone) *//*** zone.object-zone@1 (PLANNED; ADR-031 amendment 2026-10-02): one named object zone, as OpenVibe.Zone's control API returns it (GET /api/v1/zones/{zone_id}, the list under /api/v1/projects/{project_id}/zones, and the 201 of a create). Zone owns this record: its name, environment, state, access policy and per-zone quota setting. Media owns everything inside it: the objects (ordinary med_ objects in the namespace media_namespace), their keys, bytes and replicas, quota enforcement, usage counting and the one deletion path. Zone never lists keys or holds bytes. The S3 bucket name is `name`, resolved within the project and environment of the access key that signs the request, at https://s3.openvibe.media/<name>/<key>. `ovrn` is ovrn:zone:<project_id>:object-zone/<id>, with the same project_id and id as this record. The id is never reused, and neither is a name: within one project environment a name belongs to one zone for ever, deleted or not, so a signed request names exactly one zone id. Name and env are immutable. Every environment has exactly one `default` zone, whose media_namespace is the environment root. Zone creates it with the environment, and it cannot be deleted on its own. Its access is per-object, and every other zone's is private. JSON Schema cannot compare fields: contracts.zones.checkObjectZone also requires that ovrn names this project_id and id, and that media_namespace is the environment root of project_id and env, followed by .<id> unless the zone is the default. More fields may be added.nnew file mode 100644@@@ -0,0 +1,25 @@++{++ "id": "network.confirmation.manage",++ "version": "1.0",++ "owner": "network",++ "status": "planned",++ "visibility": "first-party",++ "description": "As the owning service of a sensitive capability, ask an agent's owner to confirm one action and spend the approval: create a network.confirmation-request@1 (Network derives the owner from the agent, never from the body), read it, consume it once with the digest of the action, or cancel it. Each service sees only the confirmations it created. The owner reads, approves and denies in their inbox (/api/v1/confirmations) with their own session; no token holds that. Never grantable to apps, never delegated to agents (roadmap WS-Z2).",++ "permissions": [++ "confirmations:write",++ "confirmations:read"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "admin",++ "events": [++ "network.confirmation.changed"++ ],++ "implementedBy": [++ "POST /internal/confirmations",++ "GET /internal/confirmations/:id",++ "POST /internal/confirmations/:id/consume",++ "POST /internal/confirmations/:id/cancel"++ ]++}@@@ -43,6 +43,7 @@"network.coins.debit","network.coins.read","network.coins.transfer",++ "network.confirmation.manage","network.follows.read","network.follows.write","network.integration.github.read",@@@ -66,6 +67,7 @@"network.app.created","network.app.revoked","network.block.changed",++ "network.confirmation.changed","network.credential.revoked","network.credential.rotated","network.follow.created",@@@ -1,12 +1,12 @@{"name": "openvibe-contracts",-- "version": "0.88.0",++ "version": "0.89.0","lockfileVersion": 3,"requires": true,"packages": {"": {"name": "openvibe-contracts",-- "version": "0.88.0",++ "version": "0.89.0","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.88.0",++ "version": "0.89.0","description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.","main": "index.js","types": "generated/typescript/index.d.ts",@@@ -595,6 +595,14 @@ ok(contracts.ids.newId('project').startsWith('prj_'), 'project ids use the prj pconst nodeClaims = { ...claims, sub: 'node:nod_01J0000000000000000000000Z', actor_type: 'node' };ok(V(serviceAuth.signServiceToken(nodeClaims, kp.privateKey)).ok, 'node token accepted');ok(V(serviceAuth.signServiceToken({ ...nodeClaims, sub: 'node:nod_42' }, kp.privateKey)).code === 'token.invalid_claims', 'a malformed node id is rejected');++const agentId = contracts.ids.newId('agent');++const agentClaims = { ...claims, sub: contracts.ids.principalSub({ type: 'agent', id: agentId }), actor_type: 'agent', cap: ['chat.presence.read'], cap_confirm: ['chat.message.send'], project_id: 'prj_01J0000000000000000000000Z', env: 'production', on_behalf_of: 'usr_01J0000000000000000000000Z', act: { sub: 'svc:actor' } };++const agentTok = V(serviceAuth.signServiceToken(agentClaims, kp.privateKey));++ok(agentTok.ok, 'agent token accepted');++ok(!contracts.capabilities.check(agentTok.claims, 'chat.message.send').allowed, 'a cap_confirm capability is not granted by the token alone');++ok(V(serviceAuth.signServiceToken((({ on_behalf_of, ...rest }) => rest)(agentClaims), kp.privateKey)).code === 'token.invalid_claims', 'an agent token needs on_behalf_of');++ok(V(serviceAuth.signServiceToken({ ...agentClaims, actor_type: 'service' }, kp.privateKey)).code === 'token.invalid_claims', 'an agent sub needs actor_type agent');++ok(V(serviceAuth.signServiceToken({ ...agentClaims, sub: 'agent:agt_42' }, kp.privateKey)).code === 'token.invalid_claims', 'a malformed agent id is rejected');const run = (guard, headers) => new Promise((resolve) => {const req = { headers, body: {} };
@@ -4,6 +4,35 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
@@ -4,6 +4,35 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
the notes were in the tag and commit messages (`git tag -n1`).
+## 0.89.0 — 2026-10-03
+
+**Agent tokens and confirmations** (plan T2 step 9; OpenVibe.Network `docs/t2-projects-and-grants.md` §2 gaps 1–3,
+§4 and §5). These are the contracts that Network WS-Z2 slices 7–9 wait on. All of it is additive.
+- `identity.service-token-claims` 1.3.0: `sub` also accepts `agent:agt_<ULID>` (`ids.principalSub({ type: 'agent' })`)
+ and `actor_type` gains `agent`. An agent token must carry `project_id`, `env` and `on_behalf_of` (the owner, `usr_`).
+ It may carry `cap_confirm` (capabilities held in confirm mode: each use needs an approved
+ `network.confirmation-request@1`, and they never appear in `cap`, so a receiver that does not know agents refuses
+ them) and `act` (RFC 8693 actor claim: `{ sub: 'svc:<slug>' | 'app:app_<ULID>' }`, the host, nothing else).
+ `if`/`then` rules tie an `agent:` sub to `actor_type: agent` in both directions. Service, app, mod and node
+ tokens validate as before.
+ Fixtures: valid `agent-service-host` and `agent-app-host-auto-only`; invalid `agent-without-owner`,
+ `agent-sub-as-service`, `agent-actor-with-service-sub` and `agent-bad-actor-claim`.
+- New capability `network.confirmation.manage` (planned, first-party, listed by `manifests/services/network.json`). It is
+ the owning service's side of a confirmation: create one (`POST /internal/confirmations`), read it, consume it once
+ with the action's digest, or cancel it. The owner's inbox (`/api/v1/confirmations`: list, approve, deny) runs on the
+ owner's own session, and no token holds it. The request and response bodies are Network-local until the routes are
+ built.
+- New event `network.confirmation.changed@1` (planned, produced by Network), the shape of Network's proposal
+ (`docs/contracts-proposal/network.confirmation.changed.v1.json`). Payload: `confirmation_id` (`cnf_`), `agent_id`
+ (`agt_`), `project_id`, `capability`, `audience` (the owning service), `state` (`pending | approved | denied | expired
+ | cancelled`, the names of `network.confirmation-request@1`) and `change` (`created | approved | denied | expired |
+ cancelled | used`), and optional `standing_rule`, `rule_id`, `cancel_reason`, `expires_at` and `changed_at`. `created`
+ leaves the state pending, or approved when a standing rule covers it; `used` leaves it approved; every other change
+ names its state. `cancel_reason` is required on cancelled and absent otherwise. The event never carries `summary` or
+ `details`. Fixtures: valid `requested`, `approved-by-owner`, `approved-by-standing-rule`, `used`, `cancelled` and
+ `expired`; invalid `requested-state`, `cancelled-without-reason`, `used-not-approved`, `missing-audience` and
+ `with-summary`.
+
## 0.88.0 — 2026-10-03
## 0.88.0 — 2026-10-03
**The Fabric ADR and the user-owned trust rule** (plan T1 step 3; T14 S2, the Contracts half). ADR-046 "The universal
**The Fabric ADR and the user-owned trust rule** (plan T1 step 3; T14 S2, the Contracts half). ADR-046 "The universal
@@ -23,7 +23,7 @@
@@ -23,7 +23,7 @@
},
},
{
{
"id": "identity.service-token-claims",
"id": "identity.service-token-claims",
- "version": "1.2.0",
+ "version": "1.3.0",
"owner": "network",
"owner": "network",
"schema": "identity/service-token-claims.v1.json",
"schema": "identity/service-token-claims.v1.json",
"visibility": "public",
"visibility": "public",
@@ -5552,6 +5552,16 @@
@@ -5552,6 +5552,16 @@
"status": "active",
"status": "active",
"adr": "ADR-034"
"adr": "ADR-034"
},
},
+ {
+ "id": "network.confirmation.changed",
+ "version": "1.0.0",
+ "owner": "network",
+ "schema": "events/payloads/network.confirmation.changed.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "planned",
+ "adr": "ADR-034"
+ },
{
{
"id": "zone.object-zone",
"id": "zone.object-zone",
"version": "1.0.0",
"version": "1.0.0",
new file mode 100644
new file mode 100644
@@ -0,0 +1,246 @@
@@ -0,0 +1,246 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/network.confirmation.changed.v1.json",
+ "title": "NetworkConfirmationChangedPayload",
+ "description": "network.confirmation.changed v1 (OpenVibe.Network; roadmap WS-Z2, docs/t2-projects-and-grants.md section 5; shape from OpenVibe.Network docs/contracts-proposal/network.confirmation.changed.v1.json). A confirmation request (network.confirmation-request@1) an agent raised for a sensitive capability was created, approved, denied, expired, cancelled or used. state is the state after the change, named as in network.confirmation-request@1: created leaves it pending (or approved when a standing rule covers it, with rule_id), used leaves it approved, and every other change names its state. A cancelled change says why in cancel_reason. Written to Network's outbox in the transaction that changes it, beside an audit row. Never carries the summary or details, which may hold message text: a reader that needs them asks Network with its own authority. Envelope: subject { type: confirmation, id: cnf_… }, visibility internal, on_behalf_of the owner, actor the owner who decided (or the system for an expiry, a cascade or a standing rule).",
+ "type": "object",
+ "required": [
+ "confirmation_id",
+ "agent_id",
+ "project_id",
+ "capability",
+ "audience",
+ "state",
+ "change"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "confirmation_id": {
+ "type": "string",
+ "pattern": "^cnf_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "agent_id": {
+ "type": "string",
+ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The agent that asked."
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The agent's developer project."
+ },
+ "capability": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"
+ },
+ "audience": {
+ "type": "string",
+ "pattern": "^openvibe\\.[a-z][a-z0-9-]{0,63}$",
+ "description": "The owning service of the capability: the only one that may consume or cancel the confirmation."
+ },
+ "state": {
+ "type": "string",
+ "enum": [
+ "pending",
+ "approved",
+ "denied",
+ "expired",
+ "cancelled"
+ ],
+ "description": "The state after the change. A use leaves it approved and sets used_at."
+ },
+ "change": {
+ "type": "string",
+ "enum": [
+ "created",
+ "approved",
+ "denied",
+ "expired",
+ "cancelled",
+ "used"
+ ]
+ },
+ "standing_rule": {
+ "type": "string",
+ "enum": [
+ "once",
+ "session",
+ "until",
+ "always"
+ ],
+ "description": "On approved: the rule the owner chose (once creates none)."
+ },
+ "rule_id": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "On created through a standing rule, or on approved with a session, until or always rule: that rule."
+ },
+ "cancel_reason": {
+ "type": "string",
+ "enum": [
+ "service",
+ "agent_paused",
+ "agent_revoked",
+ "grant_revoked",
+ "grant_expired",
+ "beyond_host",
+ "member_removed",
+ "account_erased",
+ "app_revoked",
+ "project_archived"
+ ],
+ "description": "On cancelled: what took the authority away."
+ },
+ "expires_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "changed_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "change": {
+ "const": "created"
+ }
+ },
+ "required": [
+ "change"
+ ]
+ },
+ "then": {
+ "properties": {
+ "state": {
+ "enum": [
+ "pending",
+ "approved"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "change": {
+ "const": "used"
+ }
+ },
+ "required": [
+ "change"
+ ]
+ },
+ "then": {
+ "properties": {
+ "state": {
+ "const": "approved"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "change": {
+ "const": "approved"
+ }
+ },
+ "required": [
+ "change"
+ ]
+ },
+ "then": {
+ "properties": {
+ "state": {
+ "const": "approved"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "change": {
+ "const": "denied"
+ }
+ },
+ "required": [
+ "change"
+ ]
+ },
+ "then": {
+ "properties": {
+ "state": {
+ "const": "denied"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "change": {
+ "const": "expired"
+ }
+ },
+ "required": [
+ "change"
+ ]
+ },
+ "then": {
+ "properties": {
+ "state": {
+ "const": "expired"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "change": {
+ "const": "cancelled"
+ }
+ },
+ "required": [
+ "change"
+ ]
+ },
+ "then": {
+ "properties": {
+ "state": {
+ "const": "cancelled"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "const": "cancelled"
+ }
+ },
+ "required": [
+ "state"
+ ]
+ },
+ "then": {
+ "required": [
+ "cancel_reason"
+ ]
+ },
+ "else": {
+ "not": {
+ "required": [
+ "cancel_reason"
+ ]
+ }
+ }
+ }
+ ]
+}
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/identity/service-token-claims.v1.json",
"$id": "https://openvibe.network/contracts/identity/service-token-claims.v1.json",
"title": "ServiceTokenClaims",
"title": "ServiceTokenClaims",
- "description": "Claims of a short-lived RS256 client-credentials token issued by OpenVibe.Network to a service, app, mod or node principal. Replaces X-Internal-Key. App tokens (actor_type app) also carry project_id and env; receivers refuse env=sandbox unless they opted in.",
+ "description": "Claims of a short-lived RS256 client-credentials token issued by OpenVibe.Network to a service, app, mod, node or agent principal. Replaces X-Internal-Key. App tokens (actor_type app) also carry project_id and env; receivers refuse env=sandbox unless they opted in. Agent tokens (actor_type agent, sub agent:agt_<ULID>) are minted for the agent's host and also carry project_id, env and on_behalf_of (the owner), and optionally cap_confirm and act.",
"type": "object",
"type": "object",
"required": [
"required": [
"iss",
"iss",
@@ -23,14 +23,15 @@
@@ -23,14 +23,15 @@
},
},
"sub": {
"sub": {
"type": "string",
"type": "string",
- "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|node:nod_[0-9A-HJKMNP-TV-Z]{26})$"
+ "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|node:nod_[0-9A-HJKMNP-TV-Z]{26}|agent:agt_[0-9A-HJKMNP-TV-Z]{26})$"
},
},
"actor_type": {
"actor_type": {
"enum": [
"enum": [
"service",
"service",
"app",
"app",
"mod",
"mod",
- "node"
+ "node",
+ "agent"
]
]
},
},
"aud": {
"aud": {
@@ -49,6 +50,14 @@
@@ -49,6 +50,14 @@
},
},
"description": "Granted capability ids; a trailing .* grants a family."
"description": "Granted capability ids; a trailing .* grants a family."
},
},
+ "cap_confirm": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"
+ },
+ "description": "Agent tokens: capabilities the agent holds in confirm mode. Each use needs an approved network.confirmation-request@1, presented as OpenVibe-Confirmation: cnf_<ULID> and consumed by the owning service. Never also in cap, so a receiver that does not know agents refuses those actions."
+ },
"ns": {
"ns": {
"type": "array",
"type": "array",
"items": {
"items": {
@@ -69,19 +78,33 @@
@@ -69,19 +78,33 @@
"project_id": {
"project_id": {
"type": "string",
"type": "string",
"pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
"pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "Developer project of an app principal (ADR-014). Services key tenancy by it. Absent on first-party service tokens."
+ "description": "Developer project of an app or agent principal (ADR-014). Services key tenancy by it. Absent on first-party service tokens."
},
},
"env": {
"env": {
"enum": [
"enum": [
"sandbox",
"sandbox",
"production"
"production"
],
],
- "description": "Environment of an app principal. A receiver MUST refuse env=sandbox (401 token.sandbox_refused) unless it opted in to sandbox tokens. Absent on first-party service tokens, which are production."
+ "description": "Environment of an app or agent principal. A receiver MUST refuse env=sandbox (401 token.sandbox_refused) unless it opted in to sandbox tokens. Absent on first-party service tokens, which are production."
},
},
"on_behalf_of": {
"on_behalf_of": {
"type": "string",
"type": "string",
"pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
"pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "The person who authorized an app through the authorization-code flow. Absent on client_credentials tokens."
+ "description": "The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Absent on client_credentials tokens."
+ },
+ "act": {
+ "type": "object",
+ "required": [
+ "sub"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "sub": {
+ "type": "string",
+ "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26})$"
+ }
+ },
+ "description": "Agent tokens: the host that runs the agent and the token was minted for (RFC 8693 actor claim), a service or an app."
}
}
},
},
"allOf": [
"allOf": [
@@ -102,6 +125,54 @@
@@ -102,6 +125,54 @@
"env"
"env"
]
]
}
}
+ },
+ {
+ "if": {
+ "properties": {
+ "actor_type": {
+ "const": "agent"
+ }
+ },
+ "required": [
+ "actor_type"
+ ]
+ },
+ "then": {
+ "required": [
+ "project_id",
+ "env",
+ "on_behalf_of"
+ ],
+ "properties": {
+ "sub": {
+ "type": "string",
+ "pattern": "^agent:agt_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "sub": {
+ "type": "string",
+ "pattern": "^agent:"
+ }
+ },
+ "required": [
+ "sub"
+ ]
+ },
+ "then": {
+ "required": [
+ "actor_type"
+ ],
+ "properties": {
+ "actor_type": {
+ "const": "agent"
+ }
+ }
+ }
}
}
]
]
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+{
+ "iss": "https://openvibe.network",
+ "sub": "svc:actor",
+ "actor_type": "agent",
+ "aud": [
+ "openvibe.chat"
+ ],
+ "cap": [
+ "chat.presence.read"
+ ],
+ "iat": 1790000000,
+ "exp": 1790000300,
+ "jti": "tok_agent005",
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "env": "production",
+ "on_behalf_of": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
+{
+ "iss": "https://openvibe.network",
+ "sub": "agent:agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "actor_type": "agent",
+ "aud": [
+ "openvibe.chat"
+ ],
+ "cap": [
+ "chat.presence.read"
+ ],
+ "iat": 1790000000,
+ "exp": 1790000300,
+ "jti": "tok_agent006",
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "env": "production",
+ "on_behalf_of": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "act": {
+ "sub": "agent:agt_01J0000000000000000000000Z"
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
+{
+ "iss": "https://openvibe.network",
+ "sub": "agent:agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "actor_type": "service",
+ "aud": [
+ "openvibe.chat"
+ ],
+ "cap": [
+ "chat.message.send"
+ ],
+ "iat": 1790000000,
+ "exp": 1790000300,
+ "jti": "tok_agent004"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
+{
+ "iss": "https://openvibe.network",
+ "sub": "agent:agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "actor_type": "agent",
+ "aud": [
+ "openvibe.chat"
+ ],
+ "cap": [],
+ "cap_confirm": [
+ "chat.message.send"
+ ],
+ "iat": 1790000000,
+ "exp": 1790000300,
+ "jti": "tok_agent003",
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "env": "production",
+ "act": {
+ "sub": "svc:actor"
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
+{
+ "iss": "https://openvibe.network",
+ "sub": "agent:agt_01J0000000000000000000000Z",
+ "actor_type": "agent",
+ "aud": [
+ "openvibe.media"
+ ],
+ "cap": [
+ "media.object.read"
+ ],
+ "ns": [
+ "prj_01J0000000000000000000000Z",
+ "app.prj_01J0000000000000000000000Z.*"
+ ],
+ "iat": 1790000000,
+ "exp": 1790000300,
+ "jti": "tok_agent002",
+ "project_id": "prj_01J0000000000000000000000Z",
+ "env": "sandbox",
+ "on_behalf_of": "usr_01J0000000000000000000000Z",
+ "act": {
+ "sub": "app:app_01J0000000000000000000000Z"
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
+{
+ "iss": "https://openvibe.network",
+ "sub": "agent:agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "actor_type": "agent",
+ "aud": [
+ "openvibe.chat"
+ ],
+ "cap": [
+ "chat.presence.read"
+ ],
+ "cap_confirm": [
+ "chat.message.send"
+ ],
+ "ns": [
+ "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ ],
+ "iat": 1790000000,
+ "exp": 1790000300,
+ "jti": "tok_agent001",
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "env": "production",
+ "on_behalf_of": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "act": {
+ "sub": "svc:actor"
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"cancelled","change":"cancelled"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","state":"pending","change":"created"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"requested","change":"created"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"pending","change":"used"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"pending","change":"created","summary":"Send 'see you at 8' to Sam"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"approved","change":"approved","standing_rule":"session","rule_id":7,"expires_at":"2026-10-03T12:15:00Z","changed_at":"2026-10-03T12:02:00Z"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"approved","change":"created","rule_id":7,"changed_at":"2026-10-03T12:05:00Z"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"cancelled","change":"cancelled","cancel_reason":"agent_revoked","changed_at":"2026-10-03T12:07:00Z"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"space.post.write","audience":"openvibe.space","state":"expired","change":"expired","changed_at":"2026-10-03T12:15:00Z"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"chat.message.send","audience":"openvibe.chat","state":"pending","change":"created","expires_at":"2026-10-03T12:15:00Z","changed_at":"2026-10-03T12:00:00Z"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"confirmation_id":"cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ","agent_id":"agt_01JAB2C3D4E5F6G7H8J9K0MNPQ","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ","capability":"network.integration.github.read","audience":"openvibe.network","state":"approved","change":"used","changed_at":"2026-10-03T12:06:00Z"}
@@ -180,14 +180,14 @@
@@ -180,14 +180,14 @@
}
}
},
},
"identity.service-token-claims": {
"identity.service-token-claims": {
- "version": "1.2.0",
+ "version": "1.3.0",
"owner": "network",
"owner": "network",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/identity/service-token-claims.v1.json",
"$id": "https://openvibe.network/contracts/identity/service-token-claims.v1.json",
"title": "ServiceTokenClaims",
"title": "ServiceTokenClaims",
- "description": "Claims of a short-lived RS256 client-credentials token issued by OpenVibe.Network to a service, app, mod or node principal. Replaces X-Internal-Key. App tokens (actor_type app) also carry project_id and env; receivers refuse env=sandbox unless they opted in.",
+ "description": "Claims of a short-lived RS256 client-credentials token issued by OpenVibe.Network to a service, app, mod, node or agent principal. Replaces X-Internal-Key. App tokens (actor_type app) also carry project_id and env; receivers refuse env=sandbox unless they opted in. Agent tokens (actor_type agent, sub agent:agt_<ULID>) are minted for the agent's host and also carry project_id, env and on_behalf_of (the owner), and optionally cap_confirm and act.",
"type": "object",
"type": "object",
"required": [
"required": [
"iss",
"iss",
@@ -208,14 +208,15 @@
@@ -208,14 +208,15 @@
},
},
"sub": {
"sub": {
"type": "string",
"type": "string",
- "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|node:nod_[0-9A-HJKMNP-TV-Z]{26})$"
+ "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|node:nod_[0-9A-HJKMNP-TV-Z]{26}|agent:agt_[0-9A-HJKMNP-TV-Z]{26})$"
},
},
"actor_type": {
"actor_type": {
"enum": [
"enum": [
"service",
"service",
"app",
"app",
"mod",
"mod",
- "node"
+ "node",
+ "agent"
]
]
},
},
"aud": {
"aud": {
@@ -234,6 +235,14 @@
@@ -234,6 +235,14 @@
},
},
"description": "Granted capability ids; a trailing .* grants a family."
"description": "Granted capability ids; a trailing .* grants a family."
},
},
+ "cap_confirm": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"
+ },
+ "description": "Agent tokens: capabilities the agent holds in confirm mode. Each use needs an approved network.confirmation-request@1, presented as OpenVibe-Confirmation: cnf_<ULID> and consumed by the owning service. Never also in cap, so a receiver that does not know agents refuses those actions."
+ },
"ns": {
"ns": {
"type": "array",
"type": "array",
"items": {
"items": {
@@ -254,19 +263,33 @@
@@ -254,19 +263,33 @@
"project_id": {
"project_id": {
"type": "string",
"type": "string",
"pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
"pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "Developer project of an app principal (ADR-014). Services key tenancy by it. Absent on first-party service tokens."
+ "description": "Developer project of an app or agent principal (ADR-014). Services key tenancy by it. Absent on first-party service tokens."
},
},
"env": {
"env": {
"enum": [
"enum": [
"sandbox",
"sandbox",
"production"
"production"
],
],
- "description": "Environment of an app principal. A receiver MUST refuse env=sandbox (401 token.sandbox_refused) unless it opted in to sandbox tokens. Absent on first-party service tokens, which are production."
+ "description": "Environment of an app or agent principal. A receiver MUST refuse env=sandbox (401 token.sandbox_refused) unless it opted in to sandbox tokens. Absent on first-party service tokens, which are production."
},
},
"on_behalf_of": {
"on_behalf_of": {
"type": "string",
"type": "string",
"pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
"pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "The person who authorized an app through the authorization-code flow. Absent on client_credentials tokens."
+ "description": "The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Absent on client_credentials tokens."
+ },
+ "act": {
+ "type": "object",
+ "required": [
+ "sub"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "sub": {
+ "type": "string",
+ "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26})$"
+ }
+ },
+ "description": "Agent tokens: the host that runs the agent and the token was minted for (RFC 8693 actor claim), a service or an app."
}
}
},
},
"allOf": [
"allOf": [
@@ -287,6 +310,54 @@
@@ -287,6 +310,54 @@
"env"
"env"
]
]
}
}
+ },
+ {
+ "if": {
+ "properties": {
+ "actor_type": {
+ "const": "agent"
+ }
+ },
+ "required": [
+ "actor_type"
+ ]
+ },
+ "then": {
+ "required": [
+ "project_id",
+ "env",
+ "on_behalf_of"
+ ],
+ "properties": {
+ "sub": {
+ "type": "string",
+ "pattern": "^agent:agt_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "sub": {
+ "type": "string",
+ "pattern": "^agent:"
+ }
+ },
+ "required": [
+ "sub"
+ ]
+ },
+ "then": {
+ "required": [
+ "actor_type"
+ ],
+ "properties": {
+ "actor_type": {
+ "const": "agent"
+ }
+ }
+ }
}
}
]
]
}
}
@@ -61835,6 +61906,257 @@
@@ -61835,6 +61906,257 @@
}
}
}
}
},
},
+ "network.confirmation.changed": {
+ "version": "1.0.0",
+ "owner": "network",
+ "status": "planned",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/network.confirmation.changed.v1.json",
+ "title": "NetworkConfirmationChangedPayload",
+ "description": "network.confirmation.changed v1 (OpenVibe.Network; roadmap WS-Z2, docs/t2-projects-and-grants.md section 5; shape from OpenVibe.Network docs/contracts-proposal/network.confirmation.changed.v1.json). A confirmation request (network.confirmation-request@1) an agent raised for a sensitive capability was created, approved, denied, expired, cancelled or used. state is the state after the change, named as in network.confirmation-request@1: created leaves it pending (or approved when a standing rule covers it, with rule_id), used leaves it approved, and every other change names its state. A cancelled change says why in cancel_reason. Written to Network's outbox in the transaction that changes it, beside an audit row. Never carries the summary or details, which may hold message text: a reader that needs them asks Network with its own authority. Envelope: subject { type: confirmation, id: cnf_… }, visibility internal, on_behalf_of the owner, actor the owner who decided (or the system for an expiry, a cascade or a standing rule).",
+ "type": "object",
+ "required": [
+ "confirmation_id",
+ "agent_id",
+ "project_id",
+ "capability",
+ "audience",
+ "state",
+ "change"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "confirmation_id": {
+ "type": "string",
+ "pattern": "^cnf_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "agent_id": {
+ "type": "string",
+ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The agent that asked."
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The agent's developer project."
+ },
+ "capability": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"
+ },
+ "audience": {
+ "type": "string",
+ "pattern": "^openvibe\\.[a-z][a-z0-9-]{0,63}$",
+ "description": "The owning service of the capability: the only one that may consume or cancel the confirmation."
+ },
+ "state": {
+ "type": "string",
+ "enum": [
+ "pending",
+ "approved",
+ "denied",
+ "expired",
+ "cancelled"
+ ],
+ "description": "The state after the change. A use leaves it approved and sets used_at."
+ },
+ "change": {
+ "type": "string",
+ "enum": [
+ "created",
+ "approved",
+ "denied",
+ "expired",
+ "cancelled",
+ "used"
+ ]
+ },
+ "standing_rule": {
+ "type": "string",
+ "enum": [
+ "once",
+ "session",
+ "until",
+ "always"
+ ],
+ "description": "On approved: the rule the owner chose (once creates none)."
+ },
+ "rule_id": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "On created through a standing rule, or on approved with a session, until or always rule: that rule."
+ },
+ "cancel_reason": {
+ "type": "string",
+ "enum": [
+ "service",
+ "agent_paused",
+ "agent_revoked",
+ "grant_revoked",
+ "grant_expired",
+ "beyond_host",
+ "member_removed",
+ "account_erased",
+ "app_revoked",
+ "project_archived"
+ ],
+ "description": "On cancelled: what took the authority away."
+ },
+ "expires_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "changed_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "change": {
+ "const": "created"
+ }
+ },
+ "required": [
+ "change"
+ ]
+ },
+ "then": {
+ "properties": {
+ "state": {
+ "enum": [
+ "pending",
+ "approved"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "change": {
+ "const": "used"
+ }
+ },
+ "required": [
+ "change"
+ ]
+ },
+ "then": {
+ "properties": {
+ "state": {
+ "const": "approved"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "change": {
+ "const": "approved"
+ }
+ },
+ "required": [
+ "change"
+ ]
+ },
+ "then": {
+ "properties": {
+ "state": {
+ "const": "approved"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "change": {
+ "const": "denied"
+ }
+ },
+ "required": [
+ "change"
+ ]
+ },
+ "then": {
+ "properties": {
+ "state": {
+ "const": "denied"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "change": {
+ "const": "expired"
+ }
+ },
+ "required": [
+ "change"
+ ]
+ },
+ "then": {
+ "properties": {
+ "state": {
+ "const": "expired"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "change": {
+ "const": "cancelled"
+ }
+ },
+ "required": [
+ "change"
+ ]
+ },
+ "then": {
+ "properties": {
+ "state": {
+ "const": "cancelled"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "state": {
+ "const": "cancelled"
+ }
+ },
+ "required": [
+ "state"
+ ]
+ },
+ "then": {
+ "required": [
+ "cancel_reason"
+ ]
+ },
+ "else": {
+ "not": {
+ "required": [
+ "cancel_reason"
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
"zone.object-zone": {
"zone.object-zone": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "zone",
"owner": "zone",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.AI API",
"title": "OpenVibe.AI API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Billing API",
"title": "OpenVibe.Billing API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Blog API",
"title": "OpenVibe.Blog API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Bot API",
"title": "OpenVibe.Bot API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Chat API",
"title": "OpenVibe.Chat API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Codes API",
"title": "OpenVibe.Codes API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Community API",
"title": "OpenVibe.Community API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Coupons API",
"title": "OpenVibe.Coupons API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Deals API",
"title": "OpenVibe.Deals API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Events API",
"title": "OpenVibe.Events API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Games API",
"title": "OpenVibe.Games API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Host API",
"title": "OpenVibe.Host API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Live API",
"title": "OpenVibe.Live API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Media API",
"title": "OpenVibe.Media API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Network API",
"title": "OpenVibe.Network API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.News API",
"title": "OpenVibe.News API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenRe.Stream API",
"title": "OpenRe.Stream API",
- "version": "0.88.0",
- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Reviews API",
"title": "OpenVibe.Reviews API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Search API",
"title": "OpenVibe.Search API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Sources API",
"title": "OpenVibe.Sources API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Space API",
"title": "OpenVibe.Space API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tips API",
"title": "OpenVibe.Tips API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tools API",
"title": "OpenVibe.Tools API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Trade API",
"title": "OpenVibe.Trade API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.VIP API",
"title": "OpenVibe.VIP API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Wiki API",
"title": "OpenVibe.Wiki API",
- "version": "0.88.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.89.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.89.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -51,9 +51,9 @@ export interface LegacyIdentityMapEntry {
@@ -51,9 +51,9 @@ export interface LegacyIdentityMapEntry {
metadata?: {};
metadata?: {};
}
}
-/** [email protected] (owner: network) */
+/** [email protected] (owner: network) */
/**
/**
- * Claims of a short-lived RS256 client-credentials token issued by OpenVibe.Network to a service, app, mod or node principal. Replaces X-Internal-Key. App tokens (actor_type app) also carry project_id and env; receivers refuse env=sandbox unless they opted in.
+ * Claims of a short-lived RS256 client-credentials token issued by OpenVibe.Network to a service, app, mod, node or agent principal. Replaces X-Internal-Key. App tokens (actor_type app) also carry project_id and env; receivers refuse env=sandbox unless they opted in. Agent tokens (actor_type agent, sub agent:agt_<ULID>) are minted for the agent's host and also carry project_id, env and on_behalf_of (the owner), and optionally cap_confirm and act.
*/
*/
export type ServiceTokenClaims = {
export type ServiceTokenClaims = {
[k: string]: unknown | undefined;
[k: string]: unknown | undefined;
@@ -63,7 +63,7 @@ export type ServiceTokenClaims = {
@@ -63,7 +63,7 @@ export type ServiceTokenClaims = {
*/
*/
iss: string;
iss: string;
sub: string;
sub: string;
- actor_type: "service" | "app" | "mod" | "node";
+ actor_type: "service" | "app" | "mod" | "node" | "agent";
/**
/**
* @minItems 1
* @minItems 1
*/
*/
@@ -72,6 +72,10 @@ export type ServiceTokenClaims = {
@@ -72,6 +72,10 @@ export type ServiceTokenClaims = {
* Granted capability ids; a trailing .* grants a family.
* Granted capability ids; a trailing .* grants a family.
*/
*/
cap: string[];
cap: string[];
+ /**
+ * Agent tokens: capabilities the agent holds in confirm mode. Each use needs an approved network.confirmation-request@1, presented as OpenVibe-Confirmation: cnf_<ULID> and consumed by the owning service. Never also in cap, so a receiver that does not know agents refuses those actions.
+ */
+ cap_confirm?: string[];
/**
/**
* Namespace constraints, e.g. live.* or mod.example.*. A developer app token carries its project_id (the whole project, as issued before namespaces were rows) and app.<project_id>.*: on Media, app.<project_id> and app.<project_id>.sandbox are the project's production and sandbox namespaces, with children below them.
* Namespace constraints, e.g. live.* or mod.example.*. A developer app token carries its project_id (the whole project, as issued before namespaces were rows) and app.<project_id>.*: on Media, app.<project_id> and app.<project_id>.sandbox are the project's production and sandbox namespaces, with children below them.
*/
*/
@@ -80,17 +84,23 @@ export type ServiceTokenClaims = {
@@ -80,17 +84,23 @@ export type ServiceTokenClaims = {
exp: number;
exp: number;
jti: string;
jti: string;
/**
/**
- * Developer project of an app principal (ADR-014). Services key tenancy by it. Absent on first-party service tokens.
+ * Developer project of an app or agent principal (ADR-014). Services key tenancy by it. Absent on first-party service tokens.
*/
*/
project_id?: string;
project_id?: string;
/**
/**
- * Environment of an app principal. A receiver MUST refuse env=sandbox (401 token.sandbox_refused) unless it opted in to sandbox tokens. Absent on first-party service tokens, which are production.
+ * Environment of an app or agent principal. A receiver MUST refuse env=sandbox (401 token.sandbox_refused) unless it opted in to sandbox tokens. Absent on first-party service tokens, which are production.
*/
*/
env?: "sandbox" | "production";
env?: "sandbox" | "production";
/**
/**
- * The person who authorized an app through the authorization-code flow. Absent on client_credentials tokens.
+ * The person who authorized an app through the authorization-code flow, or the owner an agent acts for (always present on agent tokens). Absent on client_credentials tokens.
*/
*/
on_behalf_of?: string;
on_behalf_of?: string;
+ /**
+ * Agent tokens: the host that runs the agent and the token was minted for (RFC 8693 actor claim), a service or an app.
+ */
+ act?: {
+ sub: string;
+ };
[k: string]: unknown | undefined;
[k: string]: unknown | undefined;
};
};
@@ -52018,6 +52028,58 @@ export interface ConfirmationRequest {
@@ -52018,6 +52028,58 @@ export interface ConfirmationRequest {
created_at: string;
created_at: string;
}
}
+/** [email protected] (owner: network) */
+/**
+ * network.confirmation.changed v1 (OpenVibe.Network; roadmap WS-Z2, docs/t2-projects-and-grants.md section 5; shape from OpenVibe.Network docs/contracts-proposal/network.confirmation.changed.v1.json). A confirmation request (network.confirmation-request@1) an agent raised for a sensitive capability was created, approved, denied, expired, cancelled or used. state is the state after the change, named as in network.confirmation-request@1: created leaves it pending (or approved when a standing rule covers it, with rule_id), used leaves it approved, and every other change names its state. A cancelled change says why in cancel_reason. Written to Network's outbox in the transaction that changes it, beside an audit row. Never carries the summary or details, which may hold message text: a reader that needs them asks Network with its own authority. Envelope: subject { type: confirmation, id: cnf_… }, visibility internal, on_behalf_of the owner, actor the owner who decided (or the system for an expiry, a cascade or a standing rule).
+ */
+export type NetworkConfirmationChangedPayload = {
+ [k: string]: unknown | undefined;
+} & {
+ confirmation_id: string;
+ /**
+ * The agent that asked.
+ */
+ agent_id: string;
+ /**
+ * The agent's developer project.
+ */
+ project_id: string;
+ capability: string;
+ /**
+ * The owning service of the capability: the only one that may consume or cancel the confirmation.
+ */
+ audience: string;
+ /**
+ * The state after the change. A use leaves it approved and sets used_at.
+ */
+ state: "pending" | "approved" | "denied" | "expired" | "cancelled";
+ change: "created" | "approved" | "denied" | "expired" | "cancelled" | "used";
+ /**
+ * On approved: the rule the owner chose (once creates none).
+ */
+ standing_rule?: "once" | "session" | "until" | "always";
+ /**
+ * On created through a standing rule, or on approved with a session, until or always rule: that rule.
+ */
+ rule_id?: number;
+ /**
+ * On cancelled: what took the authority away.
+ */
+ cancel_reason?:
+ | "service"
+ | "agent_paused"
+ | "agent_revoked"
+ | "grant_revoked"
+ | "grant_expired"
+ | "beyond_host"
+ | "member_removed"
+ | "account_erased"
+ | "app_revoked"
+ | "project_archived";
+ expires_at?: string;
+ changed_at?: string;
+};
+
/** [email protected] (owner: zone) */
/** [email protected] (owner: zone) */
/**
/**
* zone.object-zone@1 (PLANNED; ADR-031 amendment 2026-10-02): one named object zone, as OpenVibe.Zone's control API returns it (GET /api/v1/zones/{zone_id}, the list under /api/v1/projects/{project_id}/zones, and the 201 of a create). Zone owns this record: its name, environment, state, access policy and per-zone quota setting. Media owns everything inside it: the objects (ordinary med_ objects in the namespace media_namespace), their keys, bytes and replicas, quota enforcement, usage counting and the one deletion path. Zone never lists keys or holds bytes. The S3 bucket name is `name`, resolved within the project and environment of the access key that signs the request, at https://s3.openvibe.media/<name>/<key>. `ovrn` is ovrn:zone:<project_id>:object-zone/<id>, with the same project_id and id as this record. The id is never reused, and neither is a name: within one project environment a name belongs to one zone for ever, deleted or not, so a signed request names exactly one zone id. Name and env are immutable. Every environment has exactly one `default` zone, whose media_namespace is the environment root. Zone creates it with the environment, and it cannot be deleted on its own. Its access is per-object, and every other zone's is private. JSON Schema cannot compare fields: contracts.zones.checkObjectZone also requires that ovrn names this project_id and id, and that media_namespace is the environment root of project_id and env, followed by .<id> unless the zone is the default. More fields may be added.
* zone.object-zone@1 (PLANNED; ADR-031 amendment 2026-10-02): one named object zone, as OpenVibe.Zone's control API returns it (GET /api/v1/zones/{zone_id}, the list under /api/v1/projects/{project_id}/zones, and the 201 of a create). Zone owns this record: its name, environment, state, access policy and per-zone quota setting. Media owns everything inside it: the objects (ordinary med_ objects in the namespace media_namespace), their keys, bytes and replicas, quota enforcement, usage counting and the one deletion path. Zone never lists keys or holds bytes. The S3 bucket name is `name`, resolved within the project and environment of the access key that signs the request, at https://s3.openvibe.media/<name>/<key>. `ovrn` is ovrn:zone:<project_id>:object-zone/<id>, with the same project_id and id as this record. The id is never reused, and neither is a name: within one project environment a name belongs to one zone for ever, deleted or not, so a signed request names exactly one zone id. Name and env are immutable. Every environment has exactly one `default` zone, whose media_namespace is the environment root. Zone creates it with the environment, and it cannot be deleted on its own. Its access is per-object, and every other zone's is private. JSON Schema cannot compare fields: contracts.zones.checkObjectZone also requires that ovrn names this project_id and id, and that media_namespace is the environment root of project_id and env, followed by .<id> unless the zone is the default. More fields may be added.
new file mode 100644
new file mode 100644
@@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
+{
+ "id": "network.confirmation.manage",
+ "version": "1.0",
+ "owner": "network",
+ "status": "planned",
+ "visibility": "first-party",
+ "description": "As the owning service of a sensitive capability, ask an agent's owner to confirm one action and spend the approval: create a network.confirmation-request@1 (Network derives the owner from the agent, never from the body), read it, consume it once with the digest of the action, or cancel it. Each service sees only the confirmations it created. The owner reads, approves and denies in their inbox (/api/v1/confirmations) with their own session; no token holds that. Never grantable to apps, never delegated to agents (roadmap WS-Z2).",
+ "permissions": [
+ "confirmations:write",
+ "confirmations:read"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "admin",
+ "events": [
+ "network.confirmation.changed"
+ ],
+ "implementedBy": [
+ "POST /internal/confirmations",
+ "GET /internal/confirmations/:id",
+ "POST /internal/confirmations/:id/consume",
+ "POST /internal/confirmations/:id/cancel"
+ ]
+}
@@ -43,6 +43,7 @@
@@ -43,6 +43,7 @@
"network.coins.debit",
"network.coins.debit",
"network.coins.read",
"network.coins.read",
"network.coins.transfer",
"network.coins.transfer",
+ "network.confirmation.manage",
"network.follows.read",
"network.follows.read",
"network.follows.write",
"network.follows.write",
"network.integration.github.read",
"network.integration.github.read",
@@ -66,6 +67,7 @@
@@ -66,6 +67,7 @@
"network.app.created",
"network.app.created",
"network.app.revoked",
"network.app.revoked",
"network.block.changed",
"network.block.changed",
+ "network.confirmation.changed",
"network.credential.revoked",
"network.credential.revoked",
"network.credential.rotated",
"network.credential.rotated",
"network.follow.created",
"network.follow.created",
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.88.0",
+ "version": "0.89.0",
"lockfileVersion": 3,
"lockfileVersion": 3,
"requires": true,
"requires": true,
"packages": {
"packages": {
"": {
"": {
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.88.0",
+ "version": "0.89.0",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.88.0",
+ "version": "0.89.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"main": "index.js",
"types": "generated/typescript/index.d.ts",
"types": "generated/typescript/index.d.ts",
@@ -595,6 +595,14 @@ ok(contracts.ids.newId('project').startsWith('prj_'), 'project ids use the prj p
@@ -595,6 +595,14 @@ ok(contracts.ids.newId('project').startsWith('prj_'), 'project ids use the prj p
const nodeClaims = { ...claims, sub: 'node:nod_01J0000000000000000000000Z', actor_type: 'node' };
const nodeClaims = { ...claims, sub: 'node:nod_01J0000000000000000000000Z', actor_type: 'node' };
ok(V(serviceAuth.signServiceToken(nodeClaims, kp.privateKey)).ok, 'node token accepted');
ok(V(serviceAuth.signServiceToken(nodeClaims, kp.privateKey)).ok, 'node token accepted');
ok(V(serviceAuth.signServiceToken({ ...nodeClaims, sub: 'node:nod_42' }, kp.privateKey)).code === 'token.invalid_claims', 'a malformed node id is rejected');
ok(V(serviceAuth.signServiceToken({ ...nodeClaims, sub: 'node:nod_42' }, kp.privateKey)).code === 'token.invalid_claims', 'a malformed node id is rejected');
+const agentId = contracts.ids.newId('agent');
+const agentClaims = { ...claims, sub: contracts.ids.principalSub({ type: 'agent', id: agentId }), actor_type: 'agent', cap: ['chat.presence.read'], cap_confirm: ['chat.message.send'], project_id: 'prj_01J0000000000000000000000Z', env: 'production', on_behalf_of: 'usr_01J0000000000000000000000Z', act: { sub: 'svc:actor' } };
+const agentTok = V(serviceAuth.signServiceToken(agentClaims, kp.privateKey));
+ok(agentTok.ok, 'agent token accepted');
+ok(!contracts.capabilities.check(agentTok.claims, 'chat.message.send').allowed, 'a cap_confirm capability is not granted by the token alone');
+ok(V(serviceAuth.signServiceToken((({ on_behalf_of, ...rest }) => rest)(agentClaims), kp.privateKey)).code === 'token.invalid_claims', 'an agent token needs on_behalf_of');
+ok(V(serviceAuth.signServiceToken({ ...agentClaims, actor_type: 'service' }, kp.privateKey)).code === 'token.invalid_claims', 'an agent sub needs actor_type agent');
+ok(V(serviceAuth.signServiceToken({ ...agentClaims, sub: 'agent:agt_42' }, kp.privateKey)).code === 'token.invalid_claims', 'a malformed agent id is rejected');
const run = (guard, headers) => new Promise((resolve) => {
const run = (guard, headers) => new Promise((resolve) => {
const req = { headers, body: {} };
const req = { headers, body: {} };