openvibers/openvibe.contracts Commits

Contracts 0.77.0: agents, confirmations, the adaptive fabric and the resource index (roadmap section 4C). identity.subject-ref@1 gains the agent subject (agt_<ULID>); ids knows agt and cnf; principalSub accepts agents. events.event-envelope@1 gains optional on_behalf_of. capabilities.capability@1 gains optional sensitive, set on sixteen capabilities with external side effects. New: network.confirmation-request@1, events.delivery-policy@1 (delivery classes, per-key ordering, latency, retention tiers, routing objective, batching, residency, 64 KB ceiling), the first-party platform.* fabric contracts (resource-offer, workload-requirements, placement-result, placement-plan, rate-card, provider-state, cost-snapshot, capacity-snapshot), common.resource-summary@1, common.resource-list-result@1 and common.resource-cost@1. Fixtures and the regenerated bundle. npm test: 14065 checks passed; compat vs v0.76.0 without breaking changes.

OP OpenVibers committed 28 Sept 2026 · 7c16286d82 · 98 files, +4202 −55


Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex c378344a..2a47589e 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -6,6 +6,18 @@ the notes were in the tag and commit messages (`git tag -n1`).
## Unreleased
++## 0.77.0 — 2026-09-28
++
++**Agents, confirmations, the adaptive fabric and the resource index** (roadmap section 4C: WS-Z1, WS-Z2, WS-Z3, WS-Z7, WS-Z9).
++- `identity.subject-ref@1` gains the **agent** subject (`agt_<ULID>`): an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated. `ids` knows the `agt` and `cnf` prefixes; `principalSub` accepts agents (`agent:agt_…`).
++- `events.event-envelope@1`: optional `on_behalf_of`, the person or project an agent or app acted for.
++- `capabilities.capability@1`: optional `sensitive`. Sixteen capabilities with external side effects are marked (money movement, publishing, sending messages, deleting): an agent needs its owner's confirmation for them unless a standing rule covers it.
++- `network.confirmation-request@1`: a sensitive action waiting for its owner (pending, approved, denied, expired, cancelled; once, session, until, always).
++- `events.delivery-policy@1`: an event type's delivery class (realtime_ephemeral … webhook), durability, per-key ordering, latency targets, retention tiers, routing objective, batching, residency and a payload ceiling of 64 KB.
++- The fabric (`platform.*`, first-party): `resource-offer@1` (a node's or provider's capabilities, multidimensional capacity, latency, health, pricing), `workload-requirements@1`, `placement-result@1` (the explain answer), `placement-plan@1` (signed, expiring route plans), `rate-card@1` (a provider price in its real unit, with its free allowance, source and verification date), `provider-state@1` (usage, forecast, reserve), `cost-snapshot@1` (with counterfactuals), `capacity-snapshot@1`.
++- The resource index: `common.resource-summary@1` and `common.resource-list-result@1` (GET /api/v1/resources in every service); `common.resource-cost@1` (a charge in weighted cost units with an idempotency key).
++- Fixtures for each, and the regenerated OpenAPI and JSON Schema bundle.
++
## 0.76.0 — 2026-09-28
**The node registry** (ADR-034 section 12; roadmap WS-X1).
ddiff --git a/contracts/capabilities/capability.v1.json b/contracts/capabilities/capability.v1.json
iindex 9e4bb191..2af04424 100644
---- a/contracts/capabilities/capability.v1.json
++++ b/contracts/capabilities/capability.v1.json
@@@ -19,6 +19,7 @@
"resourceConstraints": { "type": "array", "items": { "enum": ["namespace", "owner", "room_member", "space_member", "project", "none"] } },
"quotaClass": { "type": "string" },
"events": { "type": "array", "items": { "type": "string" } },
-- "implementedBy": { "type": "array", "items": { "type": "string" }, "description": "Current route(s) that perform this action today, e.g. 'POST /api/v1/:app/files'." }
++ "implementedBy": { "type": "array", "items": { "type": "string" }, "description": "Current route(s) that perform this action today, e.g. 'POST /api/v1/:app/files'." },
++ "sensitive": { "type": "boolean", "description": "The action has an external side effect (money, sending as the person, publishing, applying, deleting, physical control): an agent needs its owner's confirmation unless a standing rule covers it (roadmap WS-Z2)." }
}
}
ddiff --git a/contracts/catalog.json b/contracts/catalog.json
iindex 35d1a0b0..48c96ae7 100644
---- a/contracts/catalog.json
++++ b/contracts/catalog.json
@@@ -5003,6 +5003,136 @@
"compatibility": "backward",
"status": "active",
"adr": "ADR-034"
++ },
++ {
++ "id": "platform.resource-offer",
++ "version": "1.0.0",
++ "owner": "network",
++ "schema": "platform/resource-offer.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-034"
++ },
++ {
++ "id": "platform.workload-requirements",
++ "version": "1.0.0",
++ "owner": "network",
++ "schema": "platform/workload-requirements.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-034"
++ },
++ {
++ "id": "platform.placement-result",
++ "version": "1.0.0",
++ "owner": "network",
++ "schema": "platform/placement-result.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-034"
++ },
++ {
++ "id": "platform.placement-plan",
++ "version": "1.0.0",
++ "owner": "network",
++ "schema": "platform/placement-plan.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-034"
++ },
++ {
++ "id": "platform.rate-card",
++ "version": "1.0.0",
++ "owner": "network",
++ "schema": "platform/rate-card.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-034"
++ },
++ {
++ "id": "platform.provider-state",
++ "version": "1.0.0",
++ "owner": "network",
++ "schema": "platform/provider-state.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-034"
++ },
++ {
++ "id": "platform.cost-snapshot",
++ "version": "1.0.0",
++ "owner": "network",
++ "schema": "platform/cost-snapshot.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-034"
++ },
++ {
++ "id": "platform.capacity-snapshot",
++ "version": "1.0.0",
++ "owner": "network",
++ "schema": "platform/capacity-snapshot.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-034"
++ },
++ {
++ "id": "events.delivery-policy",
++ "version": "1.0.0",
++ "owner": "events",
++ "schema": "events/delivery-policy.v1.json",
++ "visibility": "public",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-034"
++ },
++ {
++ "id": "common.resource-summary",
++ "version": "1.0.0",
++ "owner": "contracts",
++ "schema": "common/resource-summary.v1.json",
++ "visibility": "public",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-034"
++ },
++ {
++ "id": "common.resource-list-result",
++ "version": "1.0.0",
++ "owner": "contracts",
++ "schema": "common/resource-list-result.v1.json",
++ "visibility": "public",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-034"
++ },
++ {
++ "id": "common.resource-cost",
++ "version": "1.0.0",
++ "owner": "contracts",
++ "schema": "common/resource-cost.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-034"
++ },
++ {
++ "id": "network.confirmation-request",
++ "version": "1.0.0",
++ "owner": "network",
++ "schema": "network/confirmation-request.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-034"
}
]
}
ddiff --git a/contracts/common/resource-cost.v1.json b/contracts/common/resource-cost.v1.json
nnew file mode 100644
iindex 00000000..7fa03691
---- /dev/null
++++ b/contracts/common/resource-cost.v1.json
@@@ -0,0 +1,68 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/common/resource-cost.v1.json",
++ "title": "ResourceCost",
++ "description": "One charge in weighted cost units (roadmap WS-Z1): which subject used what, reserved and committed or released, with an idempotency key so a retried charge counts once.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "subject",
++ "unit",
++ "amount",
++ "state",
++ "idempotency_key",
++ "service",
++ "at"
++ ],
++ "properties": {
++ "subject": {
++ "$ref": "../identity/subject-ref.v1.json"
++ },
++ "project_id": {
++ "type": "string",
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "unit": {
++ "type": "string",
++ "enum": [
++ "browser-second",
++ "ai-token",
++ "ai-usd",
++ "gpu-second",
++ "video-minute",
++ "bandwidth-byte",
++ "storage-byte-day",
++ "event",
++ "watch-check",
++ "bot-control-second",
++ "job-run",
++ "upload-byte",
++ "download-byte"
++ ]
++ },
++ "amount": {
++ "type": "number",
++ "minimum": 0
++ },
++ "state": {
++ "type": "string",
++ "enum": [
++ "reserved",
++ "committed",
++ "released"
++ ]
++ },
++ "idempotency_key": {
++ "type": "string",
++ "minLength": 8,
++ "maxLength": 200
++ },
++ "service": {
++ "type": "string"
++ },
++ "at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++}
ddiff --git a/contracts/common/resource-list-result.v1.json b/contracts/common/resource-list-result.v1.json
nnew file mode 100644
iindex 00000000..1aef9549
---- /dev/null
++++ b/contracts/common/resource-list-result.v1.json
@@@ -0,0 +1,26 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/common/resource-list-result.v1.json",
++ "title": "ResourceListResult",
++ "description": "A page of the resource index (roadmap WS-Z7).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "resources",
++ "next_cursor"
++ ],
++ "properties": {
++ "resources": {
++ "type": "array",
++ "items": {
++ "$ref": "resource-summary.v1.json"
++ }
++ },
++ "next_cursor": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
++}
ddiff --git a/contracts/common/resource-summary.v1.json b/contracts/common/resource-summary.v1.json
nnew file mode 100644
iindex 00000000..7496cad1
---- /dev/null
++++ b/contracts/common/resource-summary.v1.json
@@@ -0,0 +1,75 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/common/resource-summary.v1.json",
++ "title": "ResourceSummary",
++ "description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "kind",
++ "service",
++ "state",
++ "created_at"
++ ],
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "kind": {
++ "type": "string",
++ "description": "service.kind, e.g. watch.watch, actor.actor, media.object, events.subscription"
++ },
++ "service": {
++ "type": "string"
++ },
++ "project_id": {
++ "type": "string",
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "owner": {
++ "$ref": "../identity/subject-ref.v1.json"
++ },
++ "name": {
++ "type": "string"
++ },
++ "state": {
++ "type": "string"
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "usage": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "cost units by name (WS-Z1)"
++ },
++ "links": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "self": {
++ "type": "string",
++ "format": "uri"
++ },
++ "console": {
++ "type": "string",
++ "format": "uri"
++ },
++ "docs": {
++ "type": "string",
++ "format": "uri"
++ }
++ }
++ }
++ }
++}
ddiff --git a/contracts/events/delivery-policy.v1.json b/contracts/events/delivery-policy.v1.json
nnew file mode 100644
iindex 00000000..7b4f3c56
---- /dev/null
++++ b/contracts/events/delivery-policy.v1.json
@@@ -0,0 +1,140 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/events/delivery-policy.v1.json",
++ "title": "DeliveryPolicy",
++ "description": "How an event type is carried (roadmap WS-Z3 task 2): its delivery class sets the minimum semantics, which a publisher's intent may raise and never lower; ordering is per key; the route planner picks the carrier.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "class",
++ "durability",
++ "delivery_semantics"
++ ],
++ "properties": {
++ "class": {
++ "type": "string",
++ "enum": [
++ "realtime_ephemeral",
++ "interactive",
++ "interactive_durable",
++ "domain",
++ "critical",
++ "background",
++ "bulk",
++ "webhook"
++ ]
++ },
++ "durability": {
++ "type": "string",
++ "enum": [
++ "none",
++ "optional",
++ "required"
++ ]
++ },
++ "delivery_semantics": {
++ "type": "string",
++ "enum": [
++ "at_most_once",
++ "at_least_once"
++ ]
++ },
++ "ordering": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "scope": {
++ "type": "string"
++ },
++ "key": {
++ "type": "string"
++ }
++ },
++ "required": [
++ "scope",
++ "key"
++ ]
++ },
++ "latency": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "target_p95_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "maximum_p95_ms": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "retention": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "hot": {
++ "type": "string",
++ "pattern": "^[0-9]+[hdm]$"
++ },
++ "replay": {
++ "type": "string",
++ "pattern": "^[0-9]+[hdm]$"
++ },
++ "archive": {
++ "type": "string",
++ "pattern": "^[0-9]+[hdmy]$"
++ }
++ }
++ },
++ "routing": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "objective": {
++ "type": "string",
++ "enum": [
++ "latency_first",
++ "cheapest",
++ "balanced",
++ "correctness"
++ ]
++ },
++ "multi_provider": {
++ "type": "boolean"
++ }
++ }
++ },
++ "batching": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "max_messages": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "max_delay_ms": {
++ "type": "integer",
++ "minimum": 0
++ }
++ }
++ },
++ "residency": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "inherit_project_region": {
++ "type": "boolean"
++ },
++ "region": {
++ "type": "string"
++ }
++ }
++ },
++ "max_payload_bytes": {
++ "type": "integer",
++ "minimum": 1,
++ "maximum": 65536
++ }
++ }
++}
ddiff --git a/contracts/events/event-envelope.v1.json b/contracts/events/event-envelope.v1.json
iindex 38bb1f4f..14b19cf4 100644
---- a/contracts/events/event-envelope.v1.json
++++ b/contracts/events/event-envelope.v1.json
@@@ -13,6 +13,7 @@
"version": { "type": "integer", "minimum": 1 },
"source": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$" },
"actor": { "$ref": "../identity/subject-ref.v1.json" },
++ "on_behalf_of": { "$ref": "../identity/subject-ref.v1.json" },
"timestamp": { "type": "string", "format": "date-time" },
"priority": { "enum": ["critical", "important", "low"], "default": "important" },
"visibility": { "enum": ["public", "subject", "internal"], "default": "internal" },
ddiff --git a/contracts/identity/subject-ref.v1.json b/contracts/identity/subject-ref.v1.json
iindex c5637b37..18a5a003 100644
---- a/contracts/identity/subject-ref.v1.json
++++ b/contracts/identity/subject-ref.v1.json
@@@ -96,6 +96,24 @@
}
},
"description": "PrincipalSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "agent"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
}
]
}
ddiff --git a/contracts/network/confirmation-request.v1.json b/contracts/network/confirmation-request.v1.json
nnew file mode 100644
iindex 00000000..b9642966
---- /dev/null
++++ b/contracts/network/confirmation-request.v1.json
@@@ -0,0 +1,80 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/network/confirmation-request.v1.json",
++ "title": "ConfirmationRequest",
++ "description": "A sensitive action waiting for its owner's approval (roadmap WS-Z2): an agent (or app) asks to do something with an external side effect; the owner approves or denies from any OpenVibe site; the decision is an event.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "owner",
++ "requested_by",
++ "capability",
++ "summary",
++ "state",
++ "expires_at",
++ "created_at"
++ ],
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^cnf_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "owner": {
++ "$ref": "../identity/subject-ref.v1.json"
++ },
++ "requested_by": {
++ "$ref": "../identity/subject-ref.v1.json"
++ },
++ "capability": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9-]*\\.[a-z0-9-]+\\.[a-z0-9-]+$"
++ },
++ "summary": {
++ "type": "string",
++ "maxLength": 500
++ },
++ "details": {
++ "type": "object",
++ "additionalProperties": true,
++ "properties": {}
++ },
++ "resources": {
++ "type": "array",
++ "items": {
++ "$ref": "../common/entity-ref.v1.json"
++ }
++ },
++ "state": {
++ "type": "string",
++ "enum": [
++ "pending",
++ "approved",
++ "denied",
++ "expired",
++ "cancelled"
++ ]
++ },
++ "standing_rule": {
++ "type": "string",
++ "enum": [
++ "once",
++ "session",
++ "until",
++ "always"
++ ]
++ },
++ "expires_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "decided_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++}
ddiff --git a/contracts/platform/capacity-snapshot.v1.json b/contracts/platform/capacity-snapshot.v1.json
nnew file mode 100644
iindex 00000000..70ec4c7c
---- /dev/null
++++ b/contracts/platform/capacity-snapshot.v1.json
@@@ -0,0 +1,179 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/capacity-snapshot.v1.json",
++ "title": "CapacitySnapshot",
++ "description": "A node's reported capacity at one moment, sent by its Host agent (roadmap WS-Z9 task 1).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "node_id",
++ "capacity",
++ "reported_at"
++ ],
++ "properties": {
++ "node_id": {
++ "type": "string"
++ },
++ "cell": {
++ "type": "string"
++ },
++ "capacity": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "cpu": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ },
++ "available_cores": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "memory": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ },
++ "available_mb": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "network": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "ingress_mbps": {
++ "type": "number",
++ "minimum": 0
++ },
++ "egress_mbps": {
++ "type": "number",
++ "minimum": 0
++ },
++ "available_mbps": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "disk": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "iops_available": {
++ "type": "number",
++ "minimum": 0
++ },
++ "scratch_gb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ }
++ }
++ },
++ "gpu": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "type": "string"
++ },
++ "vram_free_mb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ }
++ }
++ },
++ "workers": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "free worker slots by kind (browser, ffmpeg, ai, …)"
++ },
++ "queues": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "depth": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "oldest_ms": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "properties": {},
++ "description": "queue depth and age by name"
++ },
++ "event_loop_lag_ms": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "services": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "instances": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "active_requests": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "p50_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "p95_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "error_rate": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ }
++ }
++ },
++ "properties": {}
++ },
++ "reported_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++}
ddiff --git a/contracts/platform/cost-snapshot.v1.json b/contracts/platform/cost-snapshot.v1.json
nnew file mode 100644
iindex 00000000..a2415f7a
---- /dev/null
++++ b/contracts/platform/cost-snapshot.v1.json
@@@ -0,0 +1,79 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/cost-snapshot.v1.json",
++ "title": "CostSnapshot",
++ "description": "Cost and volume for a window, by carrier or provider, with the counterfactuals: what the same work would have cost all-local, all-provider and with the optimiser (roadmap WS-Z9 task 6).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "window_start",
++ "window_end",
++ "scope",
++ "by_target",
++ "actual_usd"
++ ],
++ "properties": {
++ "window_start": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "window_end": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "project_id": {
++ "type": "string",
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "scope": {
++ "type": "string",
++ "description": "events, jobs, ai, media, …"
++ },
++ "by_target": {
++ "type": "array",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "units": {
++ "type": "number",
++ "minimum": 0
++ },
++ "cost_usd": {
++ "type": "number",
++ "minimum": 0
++ },
++ "p50_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "p95_ms": {
++ "type": "number",
++ "minimum": 0
++ }
++ },
++ "required": [
++ "id",
++ "units",
++ "cost_usd"
++ ]
++ }
++ },
++ "actual_usd": {
++ "type": "number",
++ "minimum": 0
++ },
++ "counterfactual_usd": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "e.g. all_local, all_provider, lowest_latency"
++ }
++ }
++}
ddiff --git a/contracts/platform/placement-plan.v1.json b/contracts/platform/placement-plan.v1.json
nnew file mode 100644
iindex 00000000..4f67a678
---- /dev/null
++++ b/contracts/platform/placement-plan.v1.json
@@@ -0,0 +1,89 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/placement-plan.v1.json",
++ "title": "PlacementPlan",
++ "description": "A signed, expiring route plan (roadmap WS-Z3 and WS-Z9): which carrier or pool serves each route. Every SDK and service caches the last valid plan and keeps working while the control plane is unreachable; unsigned or expired plans are refused.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "epoch",
++ "issued_at",
++ "expires_at",
++ "issuer",
++ "key_id",
++ "routes",
++ "signature"
++ ],
++ "properties": {
++ "epoch": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "issued_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "expires_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "issuer": {
++ "type": "string"
++ },
++ "key_id": {
++ "type": "string"
++ },
++ "routes": {
++ "type": "array",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "route": {
++ "type": "string",
++ "description": "a route name: an event delivery class and scope, a workload kind, a service"
++ },
++ "targets": {
++ "type": "array",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "weight": {
++ "type": "number",
++ "minimum": 0
++ }
++ },
++ "required": [
++ "id",
++ "weight"
++ ]
++ },
++ "minItems": 1
++ },
++ "draining": {
++ "type": "array",
++ "items": {
++ "type": "string"
++ }
++ },
++ "since_epoch": {
++ "type": "integer",
++ "minimum": 1
++ }
++ },
++ "required": [
++ "route",
++ "targets"
++ ]
++ }
++ },
++ "signature": {
++ "type": "string",
++ "description": "Ed25519 over the canonical JSON of every other field, base64url"
++ }
++ }
++}
ddiff --git a/contracts/platform/placement-result.v1.json b/contracts/platform/placement-result.v1.json
nnew file mode 100644
iindex 00000000..9148fcf2
---- /dev/null
++++ b/contracts/platform/placement-result.v1.json
@@@ -0,0 +1,85 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/placement-result.v1.json",
++ "title": "PlacementResult",
++ "description": "Where a workload was (or would be) placed and why: the chosen candidate, the reasons, and every candidate with its eligibility, estimated cost and latency (POST /api/v1/placement/explain; roadmap WS-Z9).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "selected",
++ "objective",
++ "candidates",
++ "decided_at"
++ ],
++ "properties": {
++ "selected": {
++ "type": "string"
++ },
++ "objective": {
++ "type": "string",
++ "enum": [
++ "cheapest",
++ "lowest-latency",
++ "balanced",
++ "private",
++ "local-only",
++ "first-party-only",
++ "high-reliability",
++ "correctness"
++ ]
++ },
++ "reasons": {
++ "type": "array",
++ "items": {
++ "type": "string"
++ }
++ },
++ "candidates": {
++ "type": "array",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "eligible": {
++ "type": "boolean"
++ },
++ "excluded_because": {
++ "type": "string"
++ },
++ "estimated_cost_usd": {
++ "type": "number",
++ "minimum": 0
++ },
++ "estimated_latency_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "score": {
++ "type": "number"
++ },
++ "reasons": {
++ "type": "array",
++ "items": {
++ "type": "string"
++ }
++ }
++ },
++ "required": [
++ "id",
++ "eligible"
++ ]
++ }
++ },
++ "plan_epoch": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "decided_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++}
ddiff --git a/contracts/platform/provider-state.v1.json b/contracts/platform/provider-state.v1.json
nnew file mode 100644
iindex 00000000..3685977a
---- /dev/null
++++ b/contracts/platform/provider-state.v1.json
@@@ -0,0 +1,68 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/provider-state.v1.json",
++ "title": "ProviderState",
++ "description": "A provider's usage against its allowances this billing period and its forecast, used to compute marginal cost (roadmap WS-Z3 task 5).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "provider",
++ "period_start",
++ "period_end",
++ "usage",
++ "updated_at"
++ ],
++ "properties": {
++ "provider": {
++ "type": "string"
++ },
++ "period_start": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "period_end": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "usage": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "used units per rate-card metric"
++ },
++ "forecast": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "projected units at period end"
++ },
++ "reserve": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ },
++ "properties": {},
++ "description": "share of each free allowance kept for higher-priority classes"
++ },
++ "health": {
++ "type": "string",
++ "enum": [
++ "up",
++ "degraded",
++ "down"
++ ]
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++}
ddiff --git a/contracts/platform/rate-card.v1.json b/contracts/platform/rate-card.v1.json
nnew file mode 100644
iindex 00000000..c9998dbe
---- /dev/null
++++ b/contracts/platform/rate-card.v1.json
@@@ -0,0 +1,74 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/rate-card.v1.json",
++ "title": "RateCard",
++ "description": "One provider price with its free allowance, in the provider's real billing unit, with effective dates and where it was checked (roadmap WS-Z3 task 5). Changed only by review; never written by an AI on its own.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "provider",
++ "metric",
++ "unit_size",
++ "unit_price_usd",
++ "free_allowance",
++ "reset_period",
++ "effective_from",
++ "source",
++ "verified_at"
++ ],
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "provider": {
++ "type": "string"
++ },
++ "adapter": {
++ "type": "string"
++ },
++ "metric": {
++ "type": "string",
++ "description": "e.g. queue-operation-64kb, request, gib-delivered, message, gb-month, class-a-op"
++ },
++ "unit_size": {
++ "type": "number",
++ "minimum": 0
++ },
++ "unit_price_usd": {
++ "type": "number",
++ "minimum": 0
++ },
++ "free_allowance": {
++ "type": "number",
++ "minimum": 0
++ },
++ "reset_period": {
++ "type": "string",
++ "enum": [
++ "day",
++ "month",
++ "none"
++ ]
++ },
++ "region": {
++ "type": "string"
++ },
++ "effective_from": {
++ "type": "string",
++ "format": "date"
++ },
++ "effective_until": {
++ "type": "string",
++ "format": "date"
++ },
++ "source": {
++ "type": "string",
++ "format": "uri"
++ },
++ "verified_at": {
++ "type": "string",
++ "format": "date"
++ }
++ }
++}
ddiff --git a/contracts/platform/resource-offer.v1.json b/contracts/platform/resource-offer.v1.json
nnew file mode 100644
iindex 00000000..65d13788
---- /dev/null
++++ b/contracts/platform/resource-offer.v1.json
@@@ -0,0 +1,260 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/resource-offer.v1.json",
++ "title": "ResourceOffer",
++ "description": "What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.node@1) deliberately leaves out.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "offer_id",
++ "kind",
++ "region",
++ "trust",
++ "capabilities",
++ "health",
++ "pricing",
++ "updated_at"
++ ],
++ "properties": {
++ "offer_id": {
++ "type": "string"
++ },
++ "kind": {
++ "type": "string",
++ "enum": [
++ "node",
++ "provider"
++ ]
++ },
++ "node_id": {
++ "type": "string",
++ "description": "network.node@1 id, for kind node"
++ },
++ "provider": {
++ "type": "string",
++ "description": "provider or hosting company"
++ },
++ "adapter": {
++ "type": "string",
++ "description": "carrier or provider adapter id, for kind provider (e.g. nats-v1, cf-queue-v1)"
++ },
++ "region": {
++ "type": "string"
++ },
++ "cell": {
++ "type": "string"
++ },
++ "trust": {
++ "type": "string",
++ "enum": [
++ "first-party",
++ "partner",
++ "community",
++ "external"
++ ]
++ },
++ "capabilities": {
++ "type": "array",
++ "items": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"
++ },
++ "description": "e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2"
++ },
++ "capacity": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "cpu": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ },
++ "available_cores": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "memory": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ },
++ "available_mb": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "network": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "ingress_mbps": {
++ "type": "number",
++ "minimum": 0
++ },
++ "egress_mbps": {
++ "type": "number",
++ "minimum": 0
++ },
++ "available_mbps": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "disk": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "iops_available": {
++ "type": "number",
++ "minimum": 0
++ },
++ "scratch_gb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ }
++ }
++ },
++ "gpu": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "type": "string"
++ },
++ "vram_free_mb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ }
++ }
++ },
++ "workers": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "free worker slots by kind (browser, ffmpeg, ai, …)"
++ },
++ "queues": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "depth": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "oldest_ms": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "properties": {},
++ "description": "queue depth and age by name"
++ },
++ "event_loop_lag_ms": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "latency_ms": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "measured p50/p95 by operation, e.g. publish_p95, ack_p95"
++ },
++ "health": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "status": {
++ "type": "string",
++ "enum": [
++ "up",
++ "degraded",
++ "down",
++ "draining"
++ ]
++ },
++ "error_rate": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ },
++ "checked_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ },
++ "required": [
++ "status"
++ ]
++ },
++ "pricing": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "model": {
++ "type": "string",
++ "enum": [
++ "prepaid",
++ "per-request",
++ "per-byte",
++ "per-operation",
++ "per-second",
++ "free-allowance",
++ "mixed"
++ ]
++ },
++ "marginal_usd_per_unit": {
++ "type": "number",
++ "minimum": 0
++ },
++ "unit": {
++ "type": "string"
++ },
++ "rate_card": {
++ "type": "string",
++ "description": "platform.rate-card id when the price comes from one"
++ }
++ },
++ "required": [
++ "model"
++ ]
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++}
ddiff --git a/contracts/platform/workload-requirements.v1.json b/contracts/platform/workload-requirements.v1.json
nnew file mode 100644
iindex 00000000..7d8ce9b5
---- /dev/null
++++ b/contracts/platform/workload-requirements.v1.json
@@@ -0,0 +1,138 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/workload-requirements.v1.json",
++ "title": "WorkloadRequirements",
++ "description": "What a workload needs, so the platform can place it (roadmap WS-Z9). Hard constraints are filtered before any objective is scored; a cheaper candidate that misses one is never chosen.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "kind",
++ "mobility",
++ "latency_class",
++ "objective"
++ ],
++ "properties": {
++ "kind": {
++ "type": "string",
++ "description": "what runs, e.g. watch.browser, media.transcode, ai.run, events.deliver"
++ },
++ "mobility": {
++ "type": "string",
++ "enum": [
++ "request",
++ "job",
++ "session",
++ "stateful-partition"
++ ]
++ },
++ "latency_class": {
++ "type": "string",
++ "enum": [
++ "realtime",
++ "interactive",
++ "background",
++ "bulk",
++ "critical"
++ ]
++ },
++ "objective": {
++ "type": "string",
++ "enum": [
++ "cheapest",
++ "lowest-latency",
++ "balanced",
++ "private",
++ "local-only",
++ "first-party-only",
++ "high-reliability",
++ "correctness"
++ ]
++ },
++ "max_latency_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "deadline_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "max_cost_usd": {
++ "type": "number",
++ "minimum": 0
++ },
++ "durability": {
++ "type": "string",
++ "enum": [
++ "none",
++ "at-least-once",
++ "authoritative"
++ ]
++ },
++ "residency": {
++ "type": "string",
++ "description": "region or jurisdiction the work and its data must stay in"
++ },
++ "trust": {
++ "type": "array",
++ "items": {
++ "type": "string",
++ "enum": [
++ "first-party",
++ "partner",
++ "community",
++ "external"
++ ]
++ },
++ "minItems": 1,
++ "description": "trust levels allowed to run it"
++ },
++ "region": {
++ "type": "string",
++ "description": "a region, or 'nearest'"
++ },
++ "capabilities": {
++ "type": "array",
++ "items": {
++ "type": "string"
++ },
++ "description": "capabilities a candidate must offer (worker:browser, …)"
++ },
++ "resources": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "cpu": {
++ "type": "number",
++ "minimum": 0
++ },
++ "memory_mb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "gpu": {
++ "type": "boolean"
++ },
++ "scratch_gb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "egress": {
++ "type": "string",
++ "enum": [
++ "none",
++ "public",
++ "openvibe-only"
++ ]
++ }
++ }
++ },
++ "project_id": {
++ "type": "string",
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "partition_key": {
++ "type": "string",
++ "description": "stable key for rendezvous placement (room, stream, session)"
++ }
++ }
++}
ddiff --git a/fixtures/common.resource-cost/invalid/short-key.json b/fixtures/common.resource-cost/invalid/short-key.json
nnew file mode 100644
iindex 00000000..eb24023c
---- /dev/null
++++ b/fixtures/common.resource-cost/invalid/short-key.json
@@@ -0,0 +1,12 @@
++{
++ "subject": {
++ "type": "user",
++ "id": "usr_01J0000000000000000000000A"
++ },
++ "unit": "job-run",
++ "amount": 1,
++ "state": "reserved",
++ "idempotency_key": "k",
++ "service": "x",
++ "at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/common.resource-cost/invalid/unknown-unit.json b/fixtures/common.resource-cost/invalid/unknown-unit.json
nnew file mode 100644
iindex 00000000..81c2c2a2
---- /dev/null
++++ b/fixtures/common.resource-cost/invalid/unknown-unit.json
@@@ -0,0 +1,12 @@
++{
++ "subject": {
++ "type": "user",
++ "id": "usr_01J0000000000000000000000A"
++ },
++ "unit": "coins",
++ "amount": 1,
++ "state": "committed",
++ "idempotency_key": "abcdefgh",
++ "service": "x",
++ "at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/common.resource-cost/valid/browser-seconds.json b/fixtures/common.resource-cost/valid/browser-seconds.json
nnew file mode 100644
iindex 00000000..176589bc
---- /dev/null
++++ b/fixtures/common.resource-cost/valid/browser-seconds.json
@@@ -0,0 +1,13 @@
++{
++ "subject": {
++ "type": "agent",
++ "id": "agt_01J0000000000000000000000A"
++ },
++ "project_id": "prj_01J0000000000000000000000A",
++ "unit": "browser-second",
++ "amount": 12.5,
++ "state": "committed",
++ "idempotency_key": "run_01J-step-4",
++ "service": "actor",
++ "at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/common.resource-list-result/invalid/no-cursor.json b/fixtures/common.resource-list-result/invalid/no-cursor.json
nnew file mode 100644
iindex 00000000..d02ef58d
---- /dev/null
++++ b/fixtures/common.resource-list-result/invalid/no-cursor.json
@@@ -0,0 +1,3 @@
++{
++ "resources": []
++}
ddiff --git a/fixtures/common.resource-list-result/valid/page.json b/fixtures/common.resource-list-result/valid/page.json
nnew file mode 100644
iindex 00000000..27596114
---- /dev/null
++++ b/fixtures/common.resource-list-result/valid/page.json
@@@ -0,0 +1,12 @@
++{
++ "resources": [
++ {
++ "id": "sub_1",
++ "kind": "events.subscription",
++ "service": "events",
++ "state": "enabled",
++ "created_at": "2026-09-28T23:00:00Z"
++ }
++ ],
++ "next_cursor": null
++}
ddiff --git a/fixtures/common.resource-summary/invalid/no-state.json b/fixtures/common.resource-summary/invalid/no-state.json
nnew file mode 100644
iindex 00000000..7d5ca754
---- /dev/null
++++ b/fixtures/common.resource-summary/invalid/no-state.json
@@@ -0,0 +1,6 @@
++{
++ "id": "x",
++ "kind": "watch.watch",
++ "service": "watch",
++ "created_at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/common.resource-summary/valid/watch.json b/fixtures/common.resource-summary/valid/watch.json
nnew file mode 100644
iindex 00000000..9fec1a69
---- /dev/null
++++ b/fixtures/common.resource-summary/valid/watch.json
@@@ -0,0 +1,19 @@
++{
++ "id": "wch_01J0000000000000000000000A",
++ "kind": "watch.watch",
++ "service": "watch",
++ "project_id": "prj_01J0000000000000000000000A",
++ "owner": {
++ "type": "user",
++ "id": "usr_01J0000000000000000000000A"
++ },
++ "name": "Price of a camera",
++ "state": "active",
++ "created_at": "2026-09-28T23:00:00Z",
++ "usage": {
++ "watch-check": 120
++ },
++ "links": {
++ "self": "https://openvibe.watch/api/v1/watches/wch_01J0000000000000000000000A"
++ }
++}
ddiff --git a/fixtures/events.delivery-policy/invalid/huge-payload.json b/fixtures/events.delivery-policy/invalid/huge-payload.json
nnew file mode 100644
iindex 00000000..ccea726d
---- /dev/null
++++ b/fixtures/events.delivery-policy/invalid/huge-payload.json
@@@ -0,0 +1,6 @@
++{
++ "class": "domain",
++ "durability": "required",
++ "delivery_semantics": "at_least_once",
++ "max_payload_bytes": 1048576
++}
ddiff --git a/fixtures/events.delivery-policy/invalid/unknown-class.json b/fixtures/events.delivery-policy/invalid/unknown-class.json
nnew file mode 100644
iindex 00000000..df015d69
---- /dev/null
++++ b/fixtures/events.delivery-policy/invalid/unknown-class.json
@@@ -0,0 +1,5 @@
++{
++ "class": "fast",
++ "durability": "required",
++ "delivery_semantics": "at_least_once"
++}
ddiff --git a/fixtures/events.delivery-policy/valid/chat-message.json b/fixtures/events.delivery-policy/valid/chat-message.json
nnew file mode 100644
iindex 00000000..8f631a3f
---- /dev/null
++++ b/fixtures/events.delivery-policy/valid/chat-message.json
@@@ -0,0 +1,25 @@
++{
++ "class": "interactive_durable",
++ "durability": "required",
++ "delivery_semantics": "at_least_once",
++ "ordering": {
++ "scope": "room",
++ "key": "room_id"
++ },
++ "latency": {
++ "target_p95_ms": 50,
++ "maximum_p95_ms": 100
++ },
++ "retention": {
++ "hot": "7d",
++ "replay": "30d",
++ "archive": "365d"
++ },
++ "routing": {
++ "objective": "latency_first",
++ "multi_provider": true
++ },
++ "residency": {
++ "inherit_project_region": true
++ }
++}
ddiff --git a/fixtures/events.delivery-policy/valid/page-view.json b/fixtures/events.delivery-policy/valid/page-view.json
nnew file mode 100644
iindex 00000000..91ccf3cb
---- /dev/null
++++ b/fixtures/events.delivery-policy/valid/page-view.json
@@@ -0,0 +1,15 @@
++{
++ "class": "bulk",
++ "durability": "optional",
++ "delivery_semantics": "at_least_once",
++ "latency": {
++ "maximum_p95_ms": 30000
++ },
++ "routing": {
++ "objective": "cheapest"
++ },
++ "batching": {
++ "max_messages": 1000,
++ "max_delay_ms": 5000
++ }
++}
ddiff --git a/fixtures/identity.subject-ref/valid/agent.json b/fixtures/identity.subject-ref/valid/agent.json
nnew file mode 100644
iindex 00000000..d4bc059e
---- /dev/null
++++ b/fixtures/identity.subject-ref/valid/agent.json
@@@ -0,0 +1,4 @@
++{
++ "type": "agent",
++ "id": "agt_01J0000000000000000000000A"
++}
ddiff --git a/fixtures/network.confirmation-request/invalid/bad-id.json b/fixtures/network.confirmation-request/invalid/bad-id.json
nnew file mode 100644
iindex 00000000..e04edf1a
---- /dev/null
++++ b/fixtures/network.confirmation-request/invalid/bad-id.json
@@@ -0,0 +1,16 @@
++{
++ "id": "cnf_x",
++ "owner": {
++ "type": "user",
++ "id": "usr_01J0000000000000000000000A"
++ },
++ "requested_by": {
++ "type": "agent",
++ "id": "agt_01J0000000000000000000000A"
++ },
++ "capability": "a.b.c",
++ "summary": "s",
++ "state": "pending",
++ "expires_at": "2026-09-28T23:00:00Z",
++ "created_at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/network.confirmation-request/valid/apply.json b/fixtures/network.confirmation-request/valid/apply.json
nnew file mode 100644
iindex 00000000..c3cbc42a
---- /dev/null
++++ b/fixtures/network.confirmation-request/valid/apply.json
@@@ -0,0 +1,17 @@
++{
++ "id": "cnf_01J0000000000000000000000A",
++ "owner": {
++ "type": "user",
++ "id": "usr_01J0000000000000000000000A"
++ },
++ "requested_by": {
++ "type": "agent",
++ "id": "agt_01J0000000000000000000000A"
++ },
++ "capability": "work.application.submit",
++ "summary": "Submit your application to Example Co for Backend Engineer",
++ "state": "pending",
++ "standing_rule": "once",
++ "expires_at": "2026-09-29T23:00:00Z",
++ "created_at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/platform.capacity-snapshot/invalid/utilization-over-one.json b/fixtures/platform.capacity-snapshot/invalid/utilization-over-one.json
nnew file mode 100644
iindex 00000000..a4191add
---- /dev/null
++++ b/fixtures/platform.capacity-snapshot/invalid/utilization-over-one.json
@@@ -0,0 +1,9 @@
++{
++ "node_id": "n",
++ "capacity": {
++ "cpu": {
++ "utilization": 1.5
++ }
++ },
++ "reported_at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/platform.capacity-snapshot/valid/node.json b/fixtures/platform.capacity-snapshot/valid/node.json
nnew file mode 100644
iindex 00000000..a5cda985
---- /dev/null
++++ b/fixtures/platform.capacity-snapshot/valid/node.json
@@@ -0,0 +1,22 @@
++{
++ "node_id": "ovh-oregon-1",
++ "cell": "wnam-1",
++ "capacity": {
++ "cpu": {
++ "utilization": 0.37
++ },
++ "memory": {
++ "utilization": 0.51
++ },
++ "event_loop_lag_ms": 4
++ },
++ "services": {
++ "chat": {
++ "instances": 1,
++ "active_requests": 12,
++ "p95_ms": 12.8,
++ "error_rate": 0.0003
++ }
++ },
++ "reported_at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/platform.cost-snapshot/invalid/no-targets.json b/fixtures/platform.cost-snapshot/invalid/no-targets.json
nnew file mode 100644
iindex 00000000..41e89423
---- /dev/null
++++ b/fixtures/platform.cost-snapshot/invalid/no-targets.json
@@@ -0,0 +1,6 @@
++{
++ "window_start": "2026-09-28T23:00:00Z",
++ "window_end": "2026-09-28T23:00:00Z",
++ "scope": "events",
++ "actual_usd": 0
++}
ddiff --git a/fixtures/platform.cost-snapshot/valid/events-day.json b/fixtures/platform.cost-snapshot/valid/events-day.json
nnew file mode 100644
iindex 00000000..8fbafa86
---- /dev/null
++++ b/fixtures/platform.cost-snapshot/valid/events-day.json
@@@ -0,0 +1,18 @@
++{
++ "window_start": "2026-09-28T00:00:00Z",
++ "window_end": "2026-09-29T00:00:00Z",
++ "scope": "events",
++ "by_target": [
++ {
++ "id": "pg-delivery",
++ "units": 18294,
++ "cost_usd": 0,
++ "p50_ms": 11,
++ "p95_ms": 37
++ }
++ ],
++ "actual_usd": 0,
++ "counterfactual_usd": {
++ "all_provider": 0.02
++ }
++}
ddiff --git a/fixtures/platform.placement-plan/invalid/empty-targets.json b/fixtures/platform.placement-plan/invalid/empty-targets.json
nnew file mode 100644
iindex 00000000..f32497b2
---- /dev/null
++++ b/fixtures/platform.placement-plan/invalid/empty-targets.json
@@@ -0,0 +1,14 @@
++{
++ "epoch": 1,
++ "issued_at": "2026-09-28T23:00:00Z",
++ "expires_at": "2026-09-28T23:00:00Z",
++ "issuer": "network",
++ "key_id": "k",
++ "routes": [
++ {
++ "route": "r",
++ "targets": []
++ }
++ ],
++ "signature": "s"
++}
ddiff --git a/fixtures/platform.placement-plan/invalid/unsigned.json b/fixtures/platform.placement-plan/invalid/unsigned.json
nnew file mode 100644
iindex 00000000..309b4472
---- /dev/null
++++ b/fixtures/platform.placement-plan/invalid/unsigned.json
@@@ -0,0 +1,8 @@
++{
++ "epoch": 43,
++ "issued_at": "2026-09-28T23:00:00Z",
++ "expires_at": "2026-09-28T23:00:00Z",
++ "issuer": "network",
++ "key_id": "k",
++ "routes": []
++}
ddiff --git a/fixtures/platform.placement-plan/valid/signed.json b/fixtures/platform.placement-plan/valid/signed.json
nnew file mode 100644
iindex 00000000..0e0dd32e
---- /dev/null
++++ b/fixtures/platform.placement-plan/valid/signed.json
@@@ -0,0 +1,23 @@
++{
++ "epoch": 43,
++ "issued_at": "2026-09-28T23:00:00Z",
++ "expires_at": "2026-09-29T00:00:00Z",
++ "issuer": "network",
++ "key_id": "route-2026-09",
++ "routes": [
++ {
++ "route": "events:background",
++ "targets": [
++ {
++ "id": "valkey-streams",
++ "weight": 1
++ }
++ ],
++ "draining": [
++ "pg-delivery"
++ ],
++ "since_epoch": 43
++ }
++ ],
++ "signature": "c2lnbmF0dXJl"
++}
ddiff --git a/fixtures/platform.placement-result/invalid/no-candidates.json b/fixtures/platform.placement-result/invalid/no-candidates.json
nnew file mode 100644
iindex 00000000..4956e1a8
---- /dev/null
++++ b/fixtures/platform.placement-result/invalid/no-candidates.json
@@@ -0,0 +1,5 @@
++{
++ "selected": "a",
++ "objective": "balanced",
++ "decided_at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/platform.placement-result/valid/explained.json b/fixtures/platform.placement-result/valid/explained.json
nnew file mode 100644
iindex 00000000..4ffc4672
---- /dev/null
++++ b/fixtures/platform.placement-result/valid/explained.json
@@@ -0,0 +1,25 @@
++{
++ "selected": "off_oregon_1",
++ "objective": "balanced",
++ "reasons": [
++ "meets the latency target",
++ "already-paid capacity available",
++ "estimated marginal cost 0"
++ ],
++ "candidates": [
++ {
++ "id": "off_oregon_1",
++ "eligible": true,
++ "estimated_cost_usd": 0,
++ "estimated_latency_ms": 31,
++ "score": 0.041
++ },
++ {
++ "id": "off_community_331",
++ "eligible": false,
++ "excluded_because": "workload requires first-party trust"
++ }
++ ],
++ "plan_epoch": 42,
++ "decided_at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/platform.provider-state/invalid/no-usage.json b/fixtures/platform.provider-state/invalid/no-usage.json
nnew file mode 100644
iindex 00000000..63183bf8
---- /dev/null
++++ b/fixtures/platform.provider-state/invalid/no-usage.json
@@@ -0,0 +1,6 @@
++{
++ "provider": "p",
++ "period_start": "2026-09-28T23:00:00Z",
++ "period_end": "2026-09-28T23:00:00Z",
++ "updated_at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/platform.provider-state/valid/mid-month.json b/fixtures/platform.provider-state/valid/mid-month.json
nnew file mode 100644
iindex 00000000..7b4ef508
---- /dev/null
++++ b/fixtures/platform.provider-state/valid/mid-month.json
@@@ -0,0 +1,16 @@
++{
++ "provider": "example-queues",
++ "period_start": "2026-09-01T00:00:00Z",
++ "period_end": "2026-10-01T00:00:00Z",
++ "usage": {
++ "queue-operation-64kb": 430000
++ },
++ "forecast": {
++ "queue-operation-64kb": 1930000
++ },
++ "reserve": {
++ "queue-operation-64kb": 0.2
++ },
++ "health": "up",
++ "updated_at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/platform.rate-card/invalid/negative-price.json b/fixtures/platform.rate-card/invalid/negative-price.json
nnew file mode 100644
iindex 00000000..9a75c0bc
---- /dev/null
++++ b/fixtures/platform.rate-card/invalid/negative-price.json
@@@ -0,0 +1,12 @@
++{
++ "id": "rc",
++ "provider": "p",
++ "metric": "m",
++ "unit_size": 1,
++ "unit_price_usd": -1,
++ "free_allowance": 0,
++ "reset_period": "month",
++ "effective_from": "2026-09-01",
++ "source": "https://example.com",
++ "verified_at": "2026-09-28"
++}
ddiff --git a/fixtures/platform.rate-card/invalid/no-source.json b/fixtures/platform.rate-card/invalid/no-source.json
nnew file mode 100644
iindex 00000000..ec662ada
---- /dev/null
++++ b/fixtures/platform.rate-card/invalid/no-source.json
@@@ -0,0 +1,11 @@
++{
++ "id": "rc",
++ "provider": "p",
++ "metric": "m",
++ "unit_size": 1,
++ "unit_price_usd": 1,
++ "free_allowance": 0,
++ "reset_period": "month",
++ "effective_from": "2026-09-01",
++ "verified_at": "2026-09-28"
++}
ddiff --git a/fixtures/platform.rate-card/valid/queue-ops.json b/fixtures/platform.rate-card/valid/queue-ops.json
nnew file mode 100644
iindex 00000000..9a6b1602
---- /dev/null
++++ b/fixtures/platform.rate-card/valid/queue-ops.json
@@@ -0,0 +1,13 @@
++{
++ "id": "rc_example_queue_ops",
++ "provider": "example-queues",
++ "adapter": "example-queue-v1",
++ "metric": "queue-operation-64kb",
++ "unit_size": 1000000,
++ "unit_price_usd": 0.4,
++ "free_allowance": 1000000,
++ "reset_period": "month",
++ "effective_from": "2026-09-01",
++ "source": "https://example.com/pricing",
++ "verified_at": "2026-09-28"
++}
ddiff --git a/fixtures/platform.resource-offer/invalid/bad-capability.json b/fixtures/platform.resource-offer/invalid/bad-capability.json
nnew file mode 100644
iindex 00000000..7b47ef03
---- /dev/null
++++ b/fixtures/platform.resource-offer/invalid/bad-capability.json
@@@ -0,0 +1,16 @@
++{
++ "offer_id": "x",
++ "kind": "node",
++ "region": "us-west",
++ "trust": "first-party",
++ "capabilities": [
++ "Browser"
++ ],
++ "health": {
++ "status": "up"
++ },
++ "pricing": {
++ "model": "prepaid"
++ },
++ "updated_at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/platform.resource-offer/invalid/missing-health.json b/fixtures/platform.resource-offer/invalid/missing-health.json
nnew file mode 100644
iindex 00000000..fdc7b3fe
---- /dev/null
++++ b/fixtures/platform.resource-offer/invalid/missing-health.json
@@@ -0,0 +1,13 @@
++{
++ "offer_id": "x",
++ "kind": "node",
++ "region": "us-west",
++ "trust": "first-party",
++ "capabilities": [
++ "node:http"
++ ],
++ "pricing": {
++ "model": "prepaid"
++ },
++ "updated_at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/platform.resource-offer/valid/owned-node.json b/fixtures/platform.resource-offer/valid/owned-node.json
nnew file mode 100644
iindex 00000000..6dab25a9
---- /dev/null
++++ b/fixtures/platform.resource-offer/valid/owned-node.json
@@@ -0,0 +1,46 @@
++{
++ "offer_id": "off_oregon_1",
++ "kind": "node",
++ "node_id": "ovh-oregon-1",
++ "provider": "ovh",
++ "region": "us-west",
++ "cell": "wnam-1",
++ "trust": "first-party",
++ "capabilities": [
++ "node:http",
++ "worker:browser",
++ "worker:ffmpeg",
++ "events:durable"
++ ],
++ "capacity": {
++ "cpu": {
++ "utilization": 0.31,
++ "available_cores": 5.5
++ },
++ "memory": {
++ "available_mb": 18300
++ },
++ "workers": {
++ "browser": 6,
++ "ffmpeg": 3
++ },
++ "queues": {
++ "media-jobs": {
++ "depth": 2,
++ "oldest_ms": 40
++ }
++ }
++ },
++ "latency_ms": {
++ "publish_p95": 3.1
++ },
++ "health": {
++ "status": "up"
++ },
++ "pricing": {
++ "model": "prepaid",
++ "marginal_usd_per_unit": 0,
++ "unit": "job"
++ },
++ "updated_at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/platform.resource-offer/valid/provider.json b/fixtures/platform.resource-offer/valid/provider.json
nnew file mode 100644
iindex 00000000..633f8185
---- /dev/null
++++ b/fixtures/platform.resource-offer/valid/provider.json
@@@ -0,0 +1,21 @@
++{
++ "offer_id": "off_cf_queue",
++ "kind": "provider",
++ "provider": "cloudflare",
++ "adapter": "cf-queue-v1",
++ "region": "global",
++ "trust": "external",
++ "capabilities": [
++ "events:queue",
++ "events:durable"
++ ],
++ "health": {
++ "status": "up",
++ "error_rate": 0.0001
++ },
++ "pricing": {
++ "model": "per-operation",
++ "rate_card": "rc_cf_queue_ops"
++ },
++ "updated_at": "2026-09-28T23:00:00Z"
++}
ddiff --git a/fixtures/platform.workload-requirements/invalid/no-mobility.json b/fixtures/platform.workload-requirements/invalid/no-mobility.json
nnew file mode 100644
iindex 00000000..8a0f369a
---- /dev/null
++++ b/fixtures/platform.workload-requirements/invalid/no-mobility.json
@@@ -0,0 +1,5 @@
++{
++ "kind": "x",
++ "latency_class": "background",
++ "objective": "cheapest"
++}
ddiff --git a/fixtures/platform.workload-requirements/invalid/unknown-objective.json b/fixtures/platform.workload-requirements/invalid/unknown-objective.json
nnew file mode 100644
iindex 00000000..f6605190
---- /dev/null
++++ b/fixtures/platform.workload-requirements/invalid/unknown-objective.json
@@@ -0,0 +1,6 @@
++{
++ "kind": "x",
++ "mobility": "job",
++ "latency_class": "background",
++ "objective": "free"
++}
ddiff --git a/fixtures/platform.workload-requirements/valid/browser-watch.json b/fixtures/platform.workload-requirements/valid/browser-watch.json
nnew file mode 100644
iindex 00000000..f9eccb10
---- /dev/null
++++ b/fixtures/platform.workload-requirements/valid/browser-watch.json
@@@ -0,0 +1,22 @@
++{
++ "kind": "watch.browser",
++ "mobility": "job",
++ "latency_class": "interactive",
++ "objective": "balanced",
++ "max_latency_ms": 1000,
++ "max_cost_usd": 0.002,
++ "durability": "at-least-once",
++ "trust": [
++ "first-party",
++ "partner"
++ ],
++ "region": "nearest",
++ "capabilities": [
++ "worker:browser"
++ ],
++ "resources": {
++ "cpu": 2,
++ "memory_mb": 1024,
++ "egress": "public"
++ }
++}
ddiff --git a/fixtures/platform.workload-requirements/valid/ledger.json b/fixtures/platform.workload-requirements/valid/ledger.json
nnew file mode 100644
iindex 00000000..3874350d
---- /dev/null
++++ b/fixtures/platform.workload-requirements/valid/ledger.json
@@@ -0,0 +1,7 @@
++{
++ "kind": "billing.post",
++ "mobility": "stateful-partition",
++ "latency_class": "critical",
++ "objective": "correctness",
++ "durability": "authoritative"
++}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex bf511484..f54b56ea 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -103,6 +103,24 @@
}
},
"description": "PrincipalSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "agent"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
}
]
}
@@@ -1312,6 +1330,10 @@
"type": "string"
},
"description": "Current route(s) that perform this action today, e.g. 'POST /api/v1/:app/files'."
++ },
++ "sensitive": {
++ "type": "boolean",
++ "description": "The action has an external side effect (money, sending as the person, publishing, applying, deleting, physical control): an agent needs its owner's confirmation unless a standing rule covers it (roadmap WS-Z2)."
}
}
}
@@@ -1361,6 +1383,9 @@
"actor": {
"$ref": "../identity/subject-ref.v1.json"
},
++ "on_behalf_of": {
++ "$ref": "../identity/subject-ref.v1.json"
++ },
"timestamp": {
"type": "string",
"format": "date-time"
@@@ -55204,6 +55229,1432 @@
}
}
}
++ },
++ "platform.resource-offer": {
++ "version": "1.0.0",
++ "owner": "network",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/resource-offer.v1.json",
++ "title": "ResourceOffer",
++ "description": "What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.node@1) deliberately leaves out.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "offer_id",
++ "kind",
++ "region",
++ "trust",
++ "capabilities",
++ "health",
++ "pricing",
++ "updated_at"
++ ],
++ "properties": {
++ "offer_id": {
++ "type": "string"
++ },
++ "kind": {
++ "type": "string",
++ "enum": [
++ "node",
++ "provider"
++ ]
++ },
++ "node_id": {
++ "type": "string",
++ "description": "network.node@1 id, for kind node"
++ },
++ "provider": {
++ "type": "string",
++ "description": "provider or hosting company"
++ },
++ "adapter": {
++ "type": "string",
++ "description": "carrier or provider adapter id, for kind provider (e.g. nats-v1, cf-queue-v1)"
++ },
++ "region": {
++ "type": "string"
++ },
++ "cell": {
++ "type": "string"
++ },
++ "trust": {
++ "type": "string",
++ "enum": [
++ "first-party",
++ "partner",
++ "community",
++ "external"
++ ]
++ },
++ "capabilities": {
++ "type": "array",
++ "items": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"
++ },
++ "description": "e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2"
++ },
++ "capacity": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "cpu": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ },
++ "available_cores": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "memory": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ },
++ "available_mb": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "network": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "ingress_mbps": {
++ "type": "number",
++ "minimum": 0
++ },
++ "egress_mbps": {
++ "type": "number",
++ "minimum": 0
++ },
++ "available_mbps": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "disk": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "iops_available": {
++ "type": "number",
++ "minimum": 0
++ },
++ "scratch_gb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ }
++ }
++ },
++ "gpu": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "type": "string"
++ },
++ "vram_free_mb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ }
++ }
++ },
++ "workers": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "free worker slots by kind (browser, ffmpeg, ai, …)"
++ },
++ "queues": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "depth": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "oldest_ms": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "properties": {},
++ "description": "queue depth and age by name"
++ },
++ "event_loop_lag_ms": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "latency_ms": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "measured p50/p95 by operation, e.g. publish_p95, ack_p95"
++ },
++ "health": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "status": {
++ "type": "string",
++ "enum": [
++ "up",
++ "degraded",
++ "down",
++ "draining"
++ ]
++ },
++ "error_rate": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ },
++ "checked_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ },
++ "required": [
++ "status"
++ ]
++ },
++ "pricing": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "model": {
++ "type": "string",
++ "enum": [
++ "prepaid",
++ "per-request",
++ "per-byte",
++ "per-operation",
++ "per-second",
++ "free-allowance",
++ "mixed"
++ ]
++ },
++ "marginal_usd_per_unit": {
++ "type": "number",
++ "minimum": 0
++ },
++ "unit": {
++ "type": "string"
++ },
++ "rate_card": {
++ "type": "string",
++ "description": "platform.rate-card id when the price comes from one"
++ }
++ },
++ "required": [
++ "model"
++ ]
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++ }
++ },
++ "platform.workload-requirements": {
++ "version": "1.0.0",
++ "owner": "network",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/workload-requirements.v1.json",
++ "title": "WorkloadRequirements",
++ "description": "What a workload needs, so the platform can place it (roadmap WS-Z9). Hard constraints are filtered before any objective is scored; a cheaper candidate that misses one is never chosen.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "kind",
++ "mobility",
++ "latency_class",
++ "objective"
++ ],
++ "properties": {
++ "kind": {
++ "type": "string",
++ "description": "what runs, e.g. watch.browser, media.transcode, ai.run, events.deliver"
++ },
++ "mobility": {
++ "type": "string",
++ "enum": [
++ "request",
++ "job",
++ "session",
++ "stateful-partition"
++ ]
++ },
++ "latency_class": {
++ "type": "string",
++ "enum": [
++ "realtime",
++ "interactive",
++ "background",
++ "bulk",
++ "critical"
++ ]
++ },
++ "objective": {
++ "type": "string",
++ "enum": [
++ "cheapest",
++ "lowest-latency",
++ "balanced",
++ "private",
++ "local-only",
++ "first-party-only",
++ "high-reliability",
++ "correctness"
++ ]
++ },
++ "max_latency_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "deadline_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "max_cost_usd": {
++ "type": "number",
++ "minimum": 0
++ },
++ "durability": {
++ "type": "string",
++ "enum": [
++ "none",
++ "at-least-once",
++ "authoritative"
++ ]
++ },
++ "residency": {
++ "type": "string",
++ "description": "region or jurisdiction the work and its data must stay in"
++ },
++ "trust": {
++ "type": "array",
++ "items": {
++ "type": "string",
++ "enum": [
++ "first-party",
++ "partner",
++ "community",
++ "external"
++ ]
++ },
++ "minItems": 1,
++ "description": "trust levels allowed to run it"
++ },
++ "region": {
++ "type": "string",
++ "description": "a region, or 'nearest'"
++ },
++ "capabilities": {
++ "type": "array",
++ "items": {
++ "type": "string"
++ },
++ "description": "capabilities a candidate must offer (worker:browser, …)"
++ },
++ "resources": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "cpu": {
++ "type": "number",
++ "minimum": 0
++ },
++ "memory_mb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "gpu": {
++ "type": "boolean"
++ },
++ "scratch_gb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "egress": {
++ "type": "string",
++ "enum": [
++ "none",
++ "public",
++ "openvibe-only"
++ ]
++ }
++ }
++ },
++ "project_id": {
++ "type": "string",
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "partition_key": {
++ "type": "string",
++ "description": "stable key for rendezvous placement (room, stream, session)"
++ }
++ }
++ }
++ },
++ "platform.placement-result": {
++ "version": "1.0.0",
++ "owner": "network",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/placement-result.v1.json",
++ "title": "PlacementResult",
++ "description": "Where a workload was (or would be) placed and why: the chosen candidate, the reasons, and every candidate with its eligibility, estimated cost and latency (POST /api/v1/placement/explain; roadmap WS-Z9).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "selected",
++ "objective",
++ "candidates",
++ "decided_at"
++ ],
++ "properties": {
++ "selected": {
++ "type": "string"
++ },
++ "objective": {
++ "type": "string",
++ "enum": [
++ "cheapest",
++ "lowest-latency",
++ "balanced",
++ "private",
++ "local-only",
++ "first-party-only",
++ "high-reliability",
++ "correctness"
++ ]
++ },
++ "reasons": {
++ "type": "array",
++ "items": {
++ "type": "string"
++ }
++ },
++ "candidates": {
++ "type": "array",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "eligible": {
++ "type": "boolean"
++ },
++ "excluded_because": {
++ "type": "string"
++ },
++ "estimated_cost_usd": {
++ "type": "number",
++ "minimum": 0
++ },
++ "estimated_latency_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "score": {
++ "type": "number"
++ },
++ "reasons": {
++ "type": "array",
++ "items": {
++ "type": "string"
++ }
++ }
++ },
++ "required": [
++ "id",
++ "eligible"
++ ]
++ }
++ },
++ "plan_epoch": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "decided_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++ }
++ },
++ "platform.placement-plan": {
++ "version": "1.0.0",
++ "owner": "network",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/placement-plan.v1.json",
++ "title": "PlacementPlan",
++ "description": "A signed, expiring route plan (roadmap WS-Z3 and WS-Z9): which carrier or pool serves each route. Every SDK and service caches the last valid plan and keeps working while the control plane is unreachable; unsigned or expired plans are refused.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "epoch",
++ "issued_at",
++ "expires_at",
++ "issuer",
++ "key_id",
++ "routes",
++ "signature"
++ ],
++ "properties": {
++ "epoch": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "issued_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "expires_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "issuer": {
++ "type": "string"
++ },
++ "key_id": {
++ "type": "string"
++ },
++ "routes": {
++ "type": "array",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "route": {
++ "type": "string",
++ "description": "a route name: an event delivery class and scope, a workload kind, a service"
++ },
++ "targets": {
++ "type": "array",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "weight": {
++ "type": "number",
++ "minimum": 0
++ }
++ },
++ "required": [
++ "id",
++ "weight"
++ ]
++ },
++ "minItems": 1
++ },
++ "draining": {
++ "type": "array",
++ "items": {
++ "type": "string"
++ }
++ },
++ "since_epoch": {
++ "type": "integer",
++ "minimum": 1
++ }
++ },
++ "required": [
++ "route",
++ "targets"
++ ]
++ }
++ },
++ "signature": {
++ "type": "string",
++ "description": "Ed25519 over the canonical JSON of every other field, base64url"
++ }
++ }
++ }
++ },
++ "platform.rate-card": {
++ "version": "1.0.0",
++ "owner": "network",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/rate-card.v1.json",
++ "title": "RateCard",
++ "description": "One provider price with its free allowance, in the provider's real billing unit, with effective dates and where it was checked (roadmap WS-Z3 task 5). Changed only by review; never written by an AI on its own.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "provider",
++ "metric",
++ "unit_size",
++ "unit_price_usd",
++ "free_allowance",
++ "reset_period",
++ "effective_from",
++ "source",
++ "verified_at"
++ ],
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "provider": {
++ "type": "string"
++ },
++ "adapter": {
++ "type": "string"
++ },
++ "metric": {
++ "type": "string",
++ "description": "e.g. queue-operation-64kb, request, gib-delivered, message, gb-month, class-a-op"
++ },
++ "unit_size": {
++ "type": "number",
++ "minimum": 0
++ },
++ "unit_price_usd": {
++ "type": "number",
++ "minimum": 0
++ },
++ "free_allowance": {
++ "type": "number",
++ "minimum": 0
++ },
++ "reset_period": {
++ "type": "string",
++ "enum": [
++ "day",
++ "month",
++ "none"
++ ]
++ },
++ "region": {
++ "type": "string"
++ },
++ "effective_from": {
++ "type": "string",
++ "format": "date"
++ },
++ "effective_until": {
++ "type": "string",
++ "format": "date"
++ },
++ "source": {
++ "type": "string",
++ "format": "uri"
++ },
++ "verified_at": {
++ "type": "string",
++ "format": "date"
++ }
++ }
++ }
++ },
++ "platform.provider-state": {
++ "version": "1.0.0",
++ "owner": "network",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/provider-state.v1.json",
++ "title": "ProviderState",
++ "description": "A provider's usage against its allowances this billing period and its forecast, used to compute marginal cost (roadmap WS-Z3 task 5).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "provider",
++ "period_start",
++ "period_end",
++ "usage",
++ "updated_at"
++ ],
++ "properties": {
++ "provider": {
++ "type": "string"
++ },
++ "period_start": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "period_end": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "usage": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "used units per rate-card metric"
++ },
++ "forecast": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "projected units at period end"
++ },
++ "reserve": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ },
++ "properties": {},
++ "description": "share of each free allowance kept for higher-priority classes"
++ },
++ "health": {
++ "type": "string",
++ "enum": [
++ "up",
++ "degraded",
++ "down"
++ ]
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++ }
++ },
++ "platform.cost-snapshot": {
++ "version": "1.0.0",
++ "owner": "network",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/cost-snapshot.v1.json",
++ "title": "CostSnapshot",
++ "description": "Cost and volume for a window, by carrier or provider, with the counterfactuals: what the same work would have cost all-local, all-provider and with the optimiser (roadmap WS-Z9 task 6).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "window_start",
++ "window_end",
++ "scope",
++ "by_target",
++ "actual_usd"
++ ],
++ "properties": {
++ "window_start": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "window_end": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "project_id": {
++ "type": "string",
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "scope": {
++ "type": "string",
++ "description": "events, jobs, ai, media, …"
++ },
++ "by_target": {
++ "type": "array",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "units": {
++ "type": "number",
++ "minimum": 0
++ },
++ "cost_usd": {
++ "type": "number",
++ "minimum": 0
++ },
++ "p50_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "p95_ms": {
++ "type": "number",
++ "minimum": 0
++ }
++ },
++ "required": [
++ "id",
++ "units",
++ "cost_usd"
++ ]
++ }
++ },
++ "actual_usd": {
++ "type": "number",
++ "minimum": 0
++ },
++ "counterfactual_usd": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "e.g. all_local, all_provider, lowest_latency"
++ }
++ }
++ }
++ },
++ "platform.capacity-snapshot": {
++ "version": "1.0.0",
++ "owner": "network",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/platform/capacity-snapshot.v1.json",
++ "title": "CapacitySnapshot",
++ "description": "A node's reported capacity at one moment, sent by its Host agent (roadmap WS-Z9 task 1).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "node_id",
++ "capacity",
++ "reported_at"
++ ],
++ "properties": {
++ "node_id": {
++ "type": "string"
++ },
++ "cell": {
++ "type": "string"
++ },
++ "capacity": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "cpu": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ },
++ "available_cores": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "memory": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ },
++ "available_mb": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "network": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "ingress_mbps": {
++ "type": "number",
++ "minimum": 0
++ },
++ "egress_mbps": {
++ "type": "number",
++ "minimum": 0
++ },
++ "available_mbps": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "disk": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "iops_available": {
++ "type": "number",
++ "minimum": 0
++ },
++ "scratch_gb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ }
++ }
++ },
++ "gpu": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "type": "string"
++ },
++ "vram_free_mb": {
++ "type": "number",
++ "minimum": 0
++ },
++ "utilization": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ }
++ }
++ },
++ "workers": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "free worker slots by kind (browser, ffmpeg, ai, …)"
++ },
++ "queues": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "depth": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "oldest_ms": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "properties": {},
++ "description": "queue depth and age by name"
++ },
++ "event_loop_lag_ms": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "services": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "instances": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "active_requests": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "p50_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "p95_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "error_rate": {
++ "type": "number",
++ "minimum": 0,
++ "maximum": 1
++ }
++ }
++ },
++ "properties": {}
++ },
++ "reported_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++ }
++ },
++ "events.delivery-policy": {
++ "version": "1.0.0",
++ "owner": "events",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/events/delivery-policy.v1.json",
++ "title": "DeliveryPolicy",
++ "description": "How an event type is carried (roadmap WS-Z3 task 2): its delivery class sets the minimum semantics, which a publisher's intent may raise and never lower; ordering is per key; the route planner picks the carrier.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "class",
++ "durability",
++ "delivery_semantics"
++ ],
++ "properties": {
++ "class": {
++ "type": "string",
++ "enum": [
++ "realtime_ephemeral",
++ "interactive",
++ "interactive_durable",
++ "domain",
++ "critical",
++ "background",
++ "bulk",
++ "webhook"
++ ]
++ },
++ "durability": {
++ "type": "string",
++ "enum": [
++ "none",
++ "optional",
++ "required"
++ ]
++ },
++ "delivery_semantics": {
++ "type": "string",
++ "enum": [
++ "at_most_once",
++ "at_least_once"
++ ]
++ },
++ "ordering": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "scope": {
++ "type": "string"
++ },
++ "key": {
++ "type": "string"
++ }
++ },
++ "required": [
++ "scope",
++ "key"
++ ]
++ },
++ "latency": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "target_p95_ms": {
++ "type": "number",
++ "minimum": 0
++ },
++ "maximum_p95_ms": {
++ "type": "number",
++ "minimum": 0
++ }
++ }
++ },
++ "retention": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "hot": {
++ "type": "string",
++ "pattern": "^[0-9]+[hdm]$"
++ },
++ "replay": {
++ "type": "string",
++ "pattern": "^[0-9]+[hdm]$"
++ },
++ "archive": {
++ "type": "string",
++ "pattern": "^[0-9]+[hdmy]$"
++ }
++ }
++ },
++ "routing": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "objective": {
++ "type": "string",
++ "enum": [
++ "latency_first",
++ "cheapest",
++ "balanced",
++ "correctness"
++ ]
++ },
++ "multi_provider": {
++ "type": "boolean"
++ }
++ }
++ },
++ "batching": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "max_messages": {
++ "type": "integer",
++ "minimum": 1
++ },
++ "max_delay_ms": {
++ "type": "integer",
++ "minimum": 0
++ }
++ }
++ },
++ "residency": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "inherit_project_region": {
++ "type": "boolean"
++ },
++ "region": {
++ "type": "string"
++ }
++ }
++ },
++ "max_payload_bytes": {
++ "type": "integer",
++ "minimum": 1,
++ "maximum": 65536
++ }
++ }
++ }
++ },
++ "common.resource-summary": {
++ "version": "1.0.0",
++ "owner": "contracts",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/common/resource-summary.v1.json",
++ "title": "ResourceSummary",
++ "description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "kind",
++ "service",
++ "state",
++ "created_at"
++ ],
++ "properties": {
++ "id": {
++ "type": "string"
++ },
++ "kind": {
++ "type": "string",
++ "description": "service.kind, e.g. watch.watch, actor.actor, media.object, events.subscription"
++ },
++ "service": {
++ "type": "string"
++ },
++ "project_id": {
++ "type": "string",
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "owner": {
++ "$ref": "../identity/subject-ref.v1.json"
++ },
++ "name": {
++ "type": "string"
++ },
++ "state": {
++ "type": "string"
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "usage": {
++ "type": "object",
++ "additionalProperties": {
++ "type": "number",
++ "minimum": 0
++ },
++ "properties": {},
++ "description": "cost units by name (WS-Z1)"
++ },
++ "links": {
++ "type": "object",
++ "additionalProperties": false,
++ "properties": {
++ "self": {
++ "type": "string",
++ "format": "uri"
++ },
++ "console": {
++ "type": "string",
++ "format": "uri"
++ },
++ "docs": {
++ "type": "string",
++ "format": "uri"
++ }
++ }
++ }
++ }
++ }
++ },
++ "common.resource-list-result": {
++ "version": "1.0.0",
++ "owner": "contracts",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/common/resource-list-result.v1.json",
++ "title": "ResourceListResult",
++ "description": "A page of the resource index (roadmap WS-Z7).",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "resources",
++ "next_cursor"
++ ],
++ "properties": {
++ "resources": {
++ "type": "array",
++ "items": {
++ "$ref": "resource-summary.v1.json"
++ }
++ },
++ "next_cursor": {
++ "type": [
++ "string",
++ "null"
++ ]
++ }
++ }
++ }
++ },
++ "common.resource-cost": {
++ "version": "1.0.0",
++ "owner": "contracts",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/common/resource-cost.v1.json",
++ "title": "ResourceCost",
++ "description": "One charge in weighted cost units (roadmap WS-Z1): which subject used what, reserved and committed or released, with an idempotency key so a retried charge counts once.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "subject",
++ "unit",
++ "amount",
++ "state",
++ "idempotency_key",
++ "service",
++ "at"
++ ],
++ "properties": {
++ "subject": {
++ "$ref": "../identity/subject-ref.v1.json"
++ },
++ "project_id": {
++ "type": "string",
++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "unit": {
++ "type": "string",
++ "enum": [
++ "browser-second",
++ "ai-token",
++ "ai-usd",
++ "gpu-second",
++ "video-minute",
++ "bandwidth-byte",
++ "storage-byte-day",
++ "event",
++ "watch-check",
++ "bot-control-second",
++ "job-run",
++ "upload-byte",
++ "download-byte"
++ ]
++ },
++ "amount": {
++ "type": "number",
++ "minimum": 0
++ },
++ "state": {
++ "type": "string",
++ "enum": [
++ "reserved",
++ "committed",
++ "released"
++ ]
++ },
++ "idempotency_key": {
++ "type": "string",
++ "minLength": 8,
++ "maxLength": 200
++ },
++ "service": {
++ "type": "string"
++ },
++ "at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++ }
++ },
++ "network.confirmation-request": {
++ "version": "1.0.0",
++ "owner": "network",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/network/confirmation-request.v1.json",
++ "title": "ConfirmationRequest",
++ "description": "A sensitive action waiting for its owner's approval (roadmap WS-Z2): an agent (or app) asks to do something with an external side effect; the owner approves or denies from any OpenVibe site; the decision is an event.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "owner",
++ "requested_by",
++ "capability",
++ "summary",
++ "state",
++ "expires_at",
++ "created_at"
++ ],
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^cnf_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "owner": {
++ "$ref": "../identity/subject-ref.v1.json"
++ },
++ "requested_by": {
++ "$ref": "../identity/subject-ref.v1.json"
++ },
++ "capability": {
++ "type": "string",
++ "pattern": "^[a-z][a-z0-9-]*\\.[a-z0-9-]+\\.[a-z0-9-]+$"
++ },
++ "summary": {
++ "type": "string",
++ "maxLength": 500
++ },
++ "details": {
++ "type": "object",
++ "additionalProperties": true,
++ "properties": {}
++ },
++ "resources": {
++ "type": "array",
++ "items": {
++ "$ref": "../common/entity-ref.v1.json"
++ }
++ },
++ "state": {
++ "type": "string",
++ "enum": [
++ "pending",
++ "approved",
++ "denied",
++ "expired",
++ "cancelled"
++ ]
++ },
++ "standing_rule": {
++ "type": "string",
++ "enum": [
++ "once",
++ "session",
++ "until",
++ "always"
++ ]
++ },
++ "expires_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "decided_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time"
++ }
++ }
++ }
}
}
}
ddiff --git a/generated/openapi/ai.json b/generated/openapi/ai.json
iindex 60b11866..bcc9e08c 100644
---- a/generated/openapi/ai.json
++++ b/generated/openapi/ai.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.AI API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
@@@ -5066,6 +5066,24 @@
}
},
"description": "PrincipalSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "agent"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
}
]
}
ddiff --git a/generated/openapi/billing.json b/generated/openapi/billing.json
iindex bb01b9b2..ba73819d 100644
---- a/generated/openapi/billing.json
++++ b/generated/openapi/billing.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Billing API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
@@@ -3362,6 +3362,24 @@
}
},
"description": "PrincipalSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "agent"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
}
]
}
ddiff --git a/generated/openapi/blog.json b/generated/openapi/blog.json
iindex a1d1a24b..fc4bb481 100644
---- a/generated/openapi/blog.json
++++ b/generated/openapi/blog.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Blog API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/chat.json b/generated/openapi/chat.json
iindex 3866dff3..f2effb97 100644
---- a/generated/openapi/chat.json
++++ b/generated/openapi/chat.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Chat API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/codes.json b/generated/openapi/codes.json
iindex 4b0edb48..7250bad2 100644
---- a/generated/openapi/codes.json
++++ b/generated/openapi/codes.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Codes API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
@@@ -1091,6 +1091,24 @@
}
},
"description": "PrincipalSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "agent"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
}
]
},
ddiff --git a/generated/openapi/community.json b/generated/openapi/community.json
iindex 004f823b..5a0f7844 100644
---- a/generated/openapi/community.json
++++ b/generated/openapi/community.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Community API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/coupons.json b/generated/openapi/coupons.json
iindex 0c2e0b44..1987898f 100644
---- a/generated/openapi/coupons.json
++++ b/generated/openapi/coupons.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Coupons API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/deals.json b/generated/openapi/deals.json
iindex eccbe824..dff7701b 100644
---- a/generated/openapi/deals.json
++++ b/generated/openapi/deals.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Deals API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/events.json b/generated/openapi/events.json
iindex 56a61c74..3ab772d5 100644
---- a/generated/openapi/events.json
++++ b/generated/openapi/events.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Events API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
@@@ -1082,6 +1082,9 @@
"actor": {
"$ref": "#/components/schemas/identity.subject-ref.v1"
},
++ "on_behalf_of": {
++ "$ref": "#/components/schemas/identity.subject-ref.v1"
++ },
"timestamp": {
"type": "string",
"format": "date-time"
@@@ -1614,6 +1617,24 @@
}
},
"description": "PrincipalSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "agent"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
}
]
}
ddiff --git a/generated/openapi/games.json b/generated/openapi/games.json
iindex bbcd8ea6..8600b03a 100644
---- a/generated/openapi/games.json
++++ b/generated/openapi/games.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Games API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
@@@ -1033,6 +1033,24 @@
}
},
"description": "PrincipalSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "agent"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
}
]
},
ddiff --git a/generated/openapi/host.json b/generated/openapi/host.json
iindex cab586f7..1d616426 100644
---- a/generated/openapi/host.json
++++ b/generated/openapi/host.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Host API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/live.json b/generated/openapi/live.json
iindex 855b7ec7..11e14549 100644
---- a/generated/openapi/live.json
++++ b/generated/openapi/live.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Live API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/media.json b/generated/openapi/media.json
iindex 0e5af305..045f10dd 100644
---- a/generated/openapi/media.json
++++ b/generated/openapi/media.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Media API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/network.json b/generated/openapi/network.json
iindex 0bd3b640..e1c7252d 100644
---- a/generated/openapi/network.json
++++ b/generated/openapi/network.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Network API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
@@@ -1953,6 +1953,24 @@
}
},
"description": "PrincipalSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "agent"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
}
]
},
ddiff --git a/generated/openapi/news.json b/generated/openapi/news.json
iindex 6b2f15ee..f41e96f1 100644
---- a/generated/openapi/news.json
++++ b/generated/openapi/news.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.News API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/openre.json b/generated/openapi/openre.json
iindex 41aa4e11..0729115e 100644
---- a/generated/openapi/openre.json
++++ b/generated/openapi/openre.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenRe.Stream API",
-- "version": "0.76.0",
-- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
@@@ -1455,6 +1455,24 @@
}
},
"description": "PrincipalSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "agent"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
}
]
},
ddiff --git a/generated/openapi/reviews.json b/generated/openapi/reviews.json
iindex 882be129..bd6e4f5b 100644
---- a/generated/openapi/reviews.json
++++ b/generated/openapi/reviews.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Reviews API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/search.json b/generated/openapi/search.json
iindex f89c06e6..17175a89 100644
---- a/generated/openapi/search.json
++++ b/generated/openapi/search.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Search API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/sources.json b/generated/openapi/sources.json
iindex bed3b566..9b6208a0 100644
---- a/generated/openapi/sources.json
++++ b/generated/openapi/sources.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Sources API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/tips.json b/generated/openapi/tips.json
iindex fee7e54f..e50c6249 100644
---- a/generated/openapi/tips.json
++++ b/generated/openapi/tips.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tips API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
@@@ -1680,6 +1680,24 @@
}
},
"description": "PrincipalSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "agent"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
}
]
},
ddiff --git a/generated/openapi/tools.json b/generated/openapi/tools.json
iindex a0498b46..47819840 100644
---- a/generated/openapi/tools.json
++++ b/generated/openapi/tools.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tools API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/trade.json b/generated/openapi/trade.json
iindex 753da70f..14fb3ffa 100644
---- a/generated/openapi/trade.json
++++ b/generated/openapi/trade.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Trade API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/openapi/vip.json b/generated/openapi/vip.json
iindex 2a93fde0..2dbd31d8 100644
---- a/generated/openapi/vip.json
++++ b/generated/openapi/vip.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.VIP API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
@@@ -1382,6 +1382,24 @@
}
},
"description": "PrincipalSubject"
++ },
++ {
++ "type": "object",
++ "required": [
++ "type",
++ "id"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "type": {
++ "const": "agent"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
++ }
++ },
++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
}
]
},
ddiff --git a/generated/openapi/wiki.json b/generated/openapi/wiki.json
iindex c3de3c0b..4b4391f1 100644
---- a/generated/openapi/wiki.json
++++ b/generated/openapi/wiki.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Wiki API",
-- "version": "0.76.0",
-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.76.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.77.0",
++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.77.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"
ddiff --git a/generated/typescript/index.d.ts b/generated/typescript/index.d.ts
iindex be482d80..40a0c6f9 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -25,6 +25,10 @@ export type SubjectRef =
| {
type: "service" | "system";
id: string;
++ }
++ | {
++ type: "agent";
++ id: string;
};
/** [email protected] (owner: network) */
@@@ -529,6 +533,10 @@ export interface Capability {
* Current route(s) that perform this action today, e.g. 'POST /api/v1/:app/files'.
*/
implementedBy?: string[];
++ /**
++ * The action has an external side effect (money, sending as the person, publishing, applying, deleting, physical control): an agent needs its owner's confirmation unless a standing rule covers it (roadmap WS-Z2).
++ */
++ sensitive?: boolean;
}
/** [email protected] (owner: events) */
@@@ -542,6 +550,7 @@ export interface EventEnvelope {
version: number;
source: string;
actor: SubjectRef;
++ on_behalf_of?: SubjectRef;
timestamp: string;
priority?: "critical" | "important" | "low";
visibility?: "public" | "subject" | "internal";
@@@ -46278,3 +46287,497 @@ export interface NetworkNodeListResult {
nodes: NetworkNode[];
generated_at: string;
}
++
++/** [email protected] (owner: network) */
++/**
++ * What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.node@1) deliberately leaves out.
++ */
++export interface ResourceOffer {
++ offer_id: string;
++ kind: "node" | "provider";
++ /**
++ * network.node@1 id, for kind node
++ */
++ node_id?: string;
++ /**
++ * provider or hosting company
++ */
++ provider?: string;
++ /**
++ * carrier or provider adapter id, for kind provider (e.g. nats-v1, cf-queue-v1)
++ */
++ adapter?: string;
++ region: string;
++ cell?: string;
++ trust: "first-party" | "partner" | "community" | "external";
++ /**
++ * e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2
++ */
++ capabilities: string[];
++ capacity?: {
++ cpu?: {
++ utilization?: number;
++ available_cores?: number;
++ };
++ memory?: {
++ utilization?: number;
++ available_mb?: number;
++ };
++ network?: {
++ ingress_mbps?: number;
++ egress_mbps?: number;
++ available_mbps?: number;
++ };
++ disk?: {
++ iops_available?: number;
++ scratch_gb?: number;
++ utilization?: number;
++ };
++ gpu?: {
++ type?: string;
++ vram_free_mb?: number;
++ utilization?: number;
++ };
++ /**
++ * free worker slots by kind (browser, ffmpeg, ai, …)
++ */
++ workers?: {
++ [k: string]: number | undefined;
++ };
++ /**
++ * queue depth and age by name
++ */
++ queues?: {
++ [k: string]:
++ | {
++ depth?: number;
++ oldest_ms?: number;
++ }
++ | undefined;
++ };
++ event_loop_lag_ms?: number;
++ };
++ /**
++ * measured p50/p95 by operation, e.g. publish_p95, ack_p95
++ */
++ latency_ms?: {
++ [k: string]: number | undefined;
++ };
++ health: {
++ status: "up" | "degraded" | "down" | "draining";
++ error_rate?: number;
++ checked_at?: string;
++ };
++ pricing: {
++ model: "prepaid" | "per-request" | "per-byte" | "per-operation" | "per-second" | "free-allowance" | "mixed";
++ marginal_usd_per_unit?: number;
++ unit?: string;
++ /**
++ * platform.rate-card id when the price comes from one
++ */
++ rate_card?: string;
++ };
++ updated_at: string;
++}
++
++/** [email protected] (owner: network) */
++/**
++ * What a workload needs, so the platform can place it (roadmap WS-Z9). Hard constraints are filtered before any objective is scored; a cheaper candidate that misses one is never chosen.
++ */
++export interface WorkloadRequirements {
++ /**
++ * what runs, e.g. watch.browser, media.transcode, ai.run, events.deliver
++ */
++ kind: string;
++ mobility: "request" | "job" | "session" | "stateful-partition";
++ latency_class: "realtime" | "interactive" | "background" | "bulk" | "critical";
++ objective:
++ | "cheapest"
++ | "lowest-latency"
++ | "balanced"
++ | "private"
++ | "local-only"
++ | "first-party-only"
++ | "high-reliability"
++ | "correctness";
++ max_latency_ms?: number;
++ deadline_ms?: number;
++ max_cost_usd?: number;
++ durability?: "none" | "at-least-once" | "authoritative";
++ /**
++ * region or jurisdiction the work and its data must stay in
++ */
++ residency?: string;
++ /**
++ * trust levels allowed to run it
++ *
++ * @minItems 1
++ */
++ trust?: [
++ "first-party" | "partner" | "community" | "external",
++ ...("first-party" | "partner" | "community" | "external")[]
++ ];
++ /**
++ * a region, or 'nearest'
++ */
++ region?: string;
++ /**
++ * capabilities a candidate must offer (worker:browser, …)
++ */
++ capabilities?: string[];
++ resources?: {
++ cpu?: number;
++ memory_mb?: number;
++ gpu?: boolean;
++ scratch_gb?: number;
++ egress?: "none" | "public" | "openvibe-only";
++ };
++ project_id?: string;
++ /**
++ * stable key for rendezvous placement (room, stream, session)
++ */
++ partition_key?: string;
++}
++
++/** [email protected] (owner: network) */
++/**
++ * Where a workload was (or would be) placed and why: the chosen candidate, the reasons, and every candidate with its eligibility, estimated cost and latency (POST /api/v1/placement/explain; roadmap WS-Z9).
++ */
++export interface PlacementResult {
++ selected: string;
++ objective:
++ | "cheapest"
++ | "lowest-latency"
++ | "balanced"
++ | "private"
++ | "local-only"
++ | "first-party-only"
++ | "high-reliability"
++ | "correctness";
++ reasons?: string[];
++ candidates: {
++ id: string;
++ eligible: boolean;
++ excluded_because?: string;
++ estimated_cost_usd?: number;
++ estimated_latency_ms?: number;
++ score?: number;
++ reasons?: string[];
++ }[];
++ plan_epoch?: number;
++ decided_at: string;
++}
++
++/** [email protected] (owner: network) */
++/**
++ * A signed, expiring route plan (roadmap WS-Z3 and WS-Z9): which carrier or pool serves each route. Every SDK and service caches the last valid plan and keeps working while the control plane is unreachable; unsigned or expired plans are refused.
++ */
++export interface PlacementPlan {
++ epoch: number;
++ issued_at: string;
++ expires_at: string;
++ issuer: string;
++ key_id: string;
++ routes: {
++ /**
++ * a route name: an event delivery class and scope, a workload kind, a service
++ */
++ route: string;
++ /**
++ * @minItems 1
++ */
++ targets: [
++ {
++ id: string;
++ weight: number;
++ },
++ ...{
++ id: string;
++ weight: number;
++ }[]
++ ];
++ draining?: string[];
++ since_epoch?: number;
++ }[];
++ /**
++ * Ed25519 over the canonical JSON of every other field, base64url
++ */
++ signature: string;
++}
++
++/** [email protected] (owner: network) */
++/**
++ * One provider price with its free allowance, in the provider's real billing unit, with effective dates and where it was checked (roadmap WS-Z3 task 5). Changed only by review; never written by an AI on its own.
++ */
++export interface RateCard {
++ id: string;
++ provider: string;
++ adapter?: string;
++ /**
++ * e.g. queue-operation-64kb, request, gib-delivered, message, gb-month, class-a-op
++ */
++ metric: string;
++ unit_size: number;
++ unit_price_usd: number;
++ free_allowance: number;
++ reset_period: "day" | "month" | "none";
++ region?: string;
++ effective_from: string;
++ effective_until?: string;
++ source: string;
++ verified_at: string;
++}
++
++/** [email protected] (owner: network) */
++/**
++ * A provider's usage against its allowances this billing period and its forecast, used to compute marginal cost (roadmap WS-Z3 task 5).
++ */
++export interface ProviderState {
++ provider: string;
++ period_start: string;
++ period_end: string;
++ /**
++ * used units per rate-card metric
++ */
++ usage: {
++ [k: string]: number | undefined;
++ };
++ /**
++ * projected units at period end
++ */
++ forecast?: {
++ [k: string]: number | undefined;
++ };
++ /**
++ * share of each free allowance kept for higher-priority classes
++ */
++ reserve?: {
++ [k: string]: number | undefined;
++ };
++ health?: "up" | "degraded" | "down";
++ updated_at: string;
++}
++
++/** [email protected] (owner: network) */
++/**
++ * Cost and volume for a window, by carrier or provider, with the counterfactuals: what the same work would have cost all-local, all-provider and with the optimiser (roadmap WS-Z9 task 6).
++ */
++export interface CostSnapshot {
++ window_start: string;
++ window_end: string;
++ project_id?: string;
++ /**
++ * events, jobs, ai, media, …
++ */
++ scope: string;
++ by_target: {
++ id: string;
++ units: number;
++ cost_usd: number;
++ p50_ms?: number;
++ p95_ms?: number;
++ }[];
++ actual_usd: number;
++ /**
++ * e.g. all_local, all_provider, lowest_latency
++ */
++ counterfactual_usd?: {
++ [k: string]: number | undefined;
++ };
++}
++
++/** [email protected] (owner: network) */
++/**
++ * A node's reported capacity at one moment, sent by its Host agent (roadmap WS-Z9 task 1).
++ */
++export interface CapacitySnapshot {
++ node_id: string;
++ cell?: string;
++ capacity: {
++ cpu?: {
++ utilization?: number;
++ available_cores?: number;
++ };
++ memory?: {
++ utilization?: number;
++ available_mb?: number;
++ };
++ network?: {
++ ingress_mbps?: number;
++ egress_mbps?: number;
++ available_mbps?: number;
++ };
++ disk?: {
++ iops_available?: number;
++ scratch_gb?: number;
++ utilization?: number;
++ };
++ gpu?: {
++ type?: string;
++ vram_free_mb?: number;
++ utilization?: number;
++ };
++ /**
++ * free worker slots by kind (browser, ffmpeg, ai, …)
++ */
++ workers?: {
++ [k: string]: number | undefined;
++ };
++ /**
++ * queue depth and age by name
++ */
++ queues?: {
++ [k: string]:
++ | {
++ depth?: number;
++ oldest_ms?: number;
++ }
++ | undefined;
++ };
++ event_loop_lag_ms?: number;
++ };
++ services?: {
++ [k: string]:
++ | {
++ instances?: number;
++ active_requests?: number;
++ p50_ms?: number;
++ p95_ms?: number;
++ error_rate?: number;
++ }
++ | undefined;
++ };
++ reported_at: string;
++}
++
++/** [email protected] (owner: events) */
++/**
++ * How an event type is carried (roadmap WS-Z3 task 2): its delivery class sets the minimum semantics, which a publisher's intent may raise and never lower; ordering is per key; the route planner picks the carrier.
++ */
++export interface DeliveryPolicy {
++ class:
++ | "realtime_ephemeral"
++ | "interactive"
++ | "interactive_durable"
++ | "domain"
++ | "critical"
++ | "background"
++ | "bulk"
++ | "webhook";
++ durability: "none" | "optional" | "required";
++ delivery_semantics: "at_most_once" | "at_least_once";
++ ordering?: {
++ scope: string;
++ key: string;
++ };
++ latency?: {
++ target_p95_ms?: number;
++ maximum_p95_ms?: number;
++ };
++ retention?: {
++ hot?: string;
++ replay?: string;
++ archive?: string;
++ };
++ routing?: {
++ objective?: "latency_first" | "cheapest" | "balanced" | "correctness";
++ multi_provider?: boolean;
++ };
++ batching?: {
++ max_messages?: number;
++ max_delay_ms?: number;
++ };
++ residency?: {
++ inherit_project_region?: boolean;
++ region?: string;
++ };
++ max_payload_bytes?: number;
++}
++
++/** [email protected] (owner: contracts) */
++/**
++ * One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.
++ */
++export interface ResourceSummary {
++ id: string;
++ /**
++ * service.kind, e.g. watch.watch, actor.actor, media.object, events.subscription
++ */
++ kind: string;
++ service: string;
++ project_id?: string;
++ owner?: SubjectRef;
++ name?: string;
++ state: string;
++ created_at: string;
++ updated_at?: string;
++ /**
++ * cost units by name (WS-Z1)
++ */
++ usage?: {
++ [k: string]: number | undefined;
++ };
++ links?: {
++ self?: string;
++ console?: string;
++ docs?: string;
++ };
++}
++
++/** [email protected] (owner: contracts) */
++/**
++ * A page of the resource index (roadmap WS-Z7).
++ */
++export interface ResourceListResult {
++ resources: ResourceSummary[];
++ next_cursor: string | null;
++}
++
++/** [email protected] (owner: contracts) */
++/**
++ * One charge in weighted cost units (roadmap WS-Z1): which subject used what, reserved and committed or released, with an idempotency key so a retried charge counts once.
++ */
++export interface ResourceCost {
++ subject: SubjectRef;
++ project_id?: string;
++ unit:
++ | "browser-second"
++ | "ai-token"
++ | "ai-usd"
++ | "gpu-second"
++ | "video-minute"
++ | "bandwidth-byte"
++ | "storage-byte-day"
++ | "event"
++ | "watch-check"
++ | "bot-control-second"
++ | "job-run"
++ | "upload-byte"
++ | "download-byte";
++ amount: number;
++ state: "reserved" | "committed" | "released";
++ idempotency_key: string;
++ service: string;
++ at: string;
++}
++
++/** [email protected] (owner: network) */
++/**
++ * A sensitive action waiting for its owner's approval (roadmap WS-Z2): an agent (or app) asks to do something with an external side effect; the owner approves or denies from any OpenVibe site; the decision is an event.
++ */
++export interface ConfirmationRequest {
++ id: string;
++ owner: SubjectRef;
++ requested_by: SubjectRef;
++ capability: string;
++ summary: string;
++ details?: {
++ [k: string]: unknown | undefined;
++ };
++ resources?: EntityRef[];
++ state: "pending" | "approved" | "denied" | "expired" | "cancelled";
++ standing_rule?: "once" | "session" | "until" | "always";
++ expires_at: string;
++ decided_at?: string;
++ created_at: string;
++}
ddiff --git a/lib/ids.js b/lib/ids.js
iindex 1d0eb7f8..af0e0d3b 100644
---- a/lib/ids.js
++++ b/lib/ids.js
@@@ -6,11 +6,11 @@
const crypto = require('crypto');
const ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
--const PREFIX = { user: 'usr', guest: 'gst', app: 'app', mod: 'mod', event: 'evt', media: 'med', project: 'prj' };
++const PREFIX = { user: 'usr', guest: 'gst', app: 'app', mod: 'mod', agent: 'agt', event: 'evt', media: 'med', project: 'prj', confirmation: 'cnf' };
const SLUG_TYPES = new Set(['service', 'system']);
const ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
const SLUG_RE = /^[a-z][a-z0-9-]{1,39}$/;
--const SUBJECT_TYPES = ['user', 'guest', 'service', 'app', 'mod', 'system'];
++const SUBJECT_TYPES = ['user', 'guest', 'service', 'app', 'mod', 'agent', 'system'];
function ulid(now = Date.now()) {
let time = '';
@@@ -50,11 +50,11 @@ function formatSubject(ref) {
return `${ref.type}:${ref.id}`;
}
--/** Service-token `sub` claim: svc:<slug> | app:app_<ULID> | mod:mod_<ULID>. */
++/** Service-token `sub` claim: svc:<slug> | app:app_<ULID> | mod:mod_<ULID> | agent:agt_<ULID>. */
function principalSub(ref) {
if (ref.type === 'service' && SLUG_RE.test(ref.id)) return `svc:${ref.id}`;
-- if ((ref.type === 'app' || ref.type === 'mod') && isSubjectId(ref.type, ref.id)) return `${ref.type}:${ref.id}`;
-- throw new TypeError('principals are service, app or mod subjects');
++ if ((ref.type === 'app' || ref.type === 'mod' || ref.type === 'agent') && isSubjectId(ref.type, ref.id)) return `${ref.type}:${ref.id}`;
++ throw new TypeError('principals are service, app, mod or agent subjects');
}
/** Transitional Media reference for today's rows (Wave 4 introduces med_ ids). */
ddiff --git a/manifests/capabilities/billing.cashout.request.json b/manifests/capabilities/billing.cashout.request.json
iindex 9d7be2e7..1c12fa90 100644
---- a/manifests/capabilities/billing.cashout.request.json
++++ b/manifests/capabilities/billing.cashout.request.json
@@@ -4,6 +4,7 @@
"owner": "billing",
"status": "active",
"visibility": "internal",
++ "sensitive": true,
"description": "Request a cashout of creator payable into escrow (minimum and escrow period enforced by Billing), or recycle creator payable into the subject's own spendable credit. Bought credit is never withdrawable.",
"permissions": [
"billing:cashout"
ddiff --git a/manifests/capabilities/billing.intent.create.json b/manifests/capabilities/billing.intent.create.json
iindex ad10e162..e311c28f 100644
---- a/manifests/capabilities/billing.intent.create.json
++++ b/manifests/capabilities/billing.intent.create.json
@@@ -4,6 +4,7 @@
"owner": "billing",
"status": "active",
"visibility": "internal",
++ "sensitive": true,
"description": "Start a checkout: create a payment intent priced by Billing (never by the client) for a Vibes purchase or a channel subscription, and get the provider checkout URL or PowerChat app_ref. Also reads intents and captures approved PayPal orders.",
"permissions": [
"billing:intents"
ddiff --git a/manifests/capabilities/billing.subscription.manage.json b/manifests/capabilities/billing.subscription.manage.json
iindex 8874d0de..ac1ac13b 100644
---- a/manifests/capabilities/billing.subscription.manage.json
++++ b/manifests/capabilities/billing.subscription.manage.json
@@@ -4,6 +4,7 @@
"owner": "billing",
"status": "active",
"visibility": "internal",
++ "sensitive": true,
"description": "Start or renew a channel subscription paid from credit (the streamer share is credited on every period), and cancel at period end (Stripe subscriptions are cancelled at Stripe). Receipt-paid subscriptions through the API additionally need billing.ledger.admin.",
"permissions": [
"billing:subscriptions"
ddiff --git a/manifests/capabilities/billing.transfer.create.json b/manifests/capabilities/billing.transfer.create.json
iindex 837361dd..dead5e05 100644
---- a/manifests/capabilities/billing.transfer.create.json
++++ b/manifests/capabilities/billing.transfer.create.json
@@@ -4,6 +4,7 @@
"owner": "billing",
"status": "active",
"visibility": "internal",
++ "sensitive": true,
"description": "Give CREDIT to someone (tip, donation, paid interaction): user_credit of the giver to creator_payable of the recipient. Self-dealing is refused by subject. Also refunds a credit-funded transfer back to its giver (e.g. a media request that never played). Idempotent by Idempotency-Key.",
"permissions": [
"billing:transfer"
ddiff --git a/manifests/capabilities/blog.post.delete.json b/manifests/capabilities/blog.post.delete.json
iindex b78f5006..78519301 100644
---- a/manifests/capabilities/blog.post.delete.json
++++ b/manifests/capabilities/blog.post.delete.json
@@@ -4,6 +4,7 @@
"owner": "blog",
"status": "active",
"visibility": "first-party",
++ "sensitive": true,
"description": "Delete a post: readers get 410, Search a tombstone, the Community thread is hidden; immutable revisions are kept. Editors, owners, the author, and staff (moderation).",
"permissions": [
"blog:write"
ddiff --git a/manifests/capabilities/blog.post.publish.json b/manifests/capabilities/blog.post.publish.json
iindex 960890e0..aaca3c73 100644
---- a/manifests/capabilities/blog.post.publish.json
++++ b/manifests/capabilities/blog.post.publish.json
@@@ -4,6 +4,7 @@
"owner": "blog",
"status": "active",
"visibility": "first-party",
++ "sensitive": true,
"description": "Make a revision the published one (idempotent). AI-generated revisions are refused (409 post.review_required) until a person has recorded an approving review.",
"permissions": [
"blog:write"
ddiff --git a/manifests/capabilities/chat.message.send.json b/manifests/capabilities/chat.message.send.json
iindex 7f183c30..208828dc 100644
---- a/manifests/capabilities/chat.message.send.json
++++ b/manifests/capabilities/chat.message.send.json
@@@ -4,6 +4,7 @@
"owner": "chat",
"status": "active",
"visibility": "first-party",
++ "sensitive": true,
"description": "Send a chat message to a room the actor is a member of (browsers over the chat socket; first-party services over Chat's service API).",
"permissions": [
"chat:write"
ddiff --git a/manifests/capabilities/community.comment.write.json b/manifests/capabilities/community.comment.write.json
iindex be4bb8c4..a4c304fa 100644
---- a/manifests/capabilities/community.comment.write.json
++++ b/manifests/capabilities/community.comment.write.json
@@@ -4,6 +4,7 @@
"owner": "community",
"status": "active",
"visibility": "first-party",
++ "sensitive": true,
"description": "Typed comment threads as a service: resolve the thread of any EntityRef (browsers are limited to an allowlist of types), and comment, vote and delete one's own comments as the acting subject (X-OV-Subject), or comment as origin \"ai\" (never attributed to a person, roadmap 33). Ownership and limits are checked by Community.",
"inputSchema": "community.comment-write-request@1",
"outputSchema": "community.comment-write-result@1",
ddiff --git a/manifests/capabilities/community.post.create.json b/manifests/capabilities/community.post.create.json
iindex 4eeff517..879f613d 100644
---- a/manifests/capabilities/community.post.create.json
++++ b/manifests/capabilities/community.post.create.json
@@@ -4,6 +4,7 @@
"owner": "community",
"status": "active",
"visibility": "public",
++ "sensitive": true,
"description": "Forum writes as a service: create threads and replies in a space, vote on threads, and edit or delete one's own posts, as the acting subject (X-OV-Subject) or as origin \"ai\" (no author, labelled as AI). Space visibility (public, members, staff), locks and limits are checked by Community.",
"outputSchema": "community.post-write-result@1",
"permissions": [
ddiff --git a/manifests/capabilities/media.object.delete.json b/manifests/capabilities/media.object.delete.json
iindex 9b49d313..1417ac19 100644
---- a/manifests/capabilities/media.object.delete.json
++++ b/manifests/capabilities/media.object.delete.json
@@@ -4,6 +4,7 @@
"owner": "media",
"status": "active",
"visibility": "public",
++ "sensitive": true,
"description": "Delete an object in a namespace (the delete verb of Media's namespace grants, WS-G task 2): a soft delete that keeps the bytes for the retention period, and the restore that undoes it within that period (checked against the namespace's quotas); refused while the object is under a retention hold. The same verb guards the v1 file delete (DELETE /api/v1/:app/files/:key). media.object.upload also grants it, for tokens issued before the verbs were split, unless the namespace's policy is strict_verbs.",
"inputSchema": "common.no-body@1",
"outputSchema": "media.object@1",
ddiff --git a/manifests/capabilities/network.coins.transfer.json b/manifests/capabilities/network.coins.transfer.json
iindex d81cd4ff..5e201dea 100644
---- a/manifests/capabilities/network.coins.transfer.json
++++ b/manifests/capabilities/network.coins.transfer.json
@@@ -4,6 +4,7 @@
"owner": "network",
"status": "active",
"visibility": "internal",
++ "sensitive": true,
"description": "Atomically move OpenCoins between two Network users. A token may only act for its own app_id.",
"permissions": [
"coins:write"
ddiff --git a/manifests/capabilities/news.story.publish.json b/manifests/capabilities/news.story.publish.json
iindex 03dc661a..6565c1b8 100644
---- a/manifests/capabilities/news.story.publish.json
++++ b/manifests/capabilities/news.story.publish.json
@@@ -4,6 +4,7 @@
"owner": "news",
"status": "active",
"visibility": "first-party",
++ "sensitive": true,
"description": "Publish a revision (refused while it cites no source, rests on a source removed upstream or detached, or is AI-generated/AI-assisted without a person's approval), or unpublish a story. Publishing resolves the upstream flags the revision answers and publishes pending correction/update notes.",
"permissions": [
"news:write"
ddiff --git a/manifests/capabilities/reviews.summary.publish.json b/manifests/capabilities/reviews.summary.publish.json
iindex 22781fd9..9475c099 100644
---- a/manifests/capabilities/reviews.summary.publish.json
++++ b/manifests/capabilities/reviews.summary.publish.json
@@@ -4,6 +4,7 @@
"owner": "reviews",
"status": "active",
"visibility": "first-party",
++ "sensitive": true,
"description": "Write, review (approve or reject), publish and unpublish summary revisions as the editor named in X-OV-Subject. Publishing refuses AI or system-prepared revisions without a person's approval and any revision with a point that cites no live signal of the entity.",
"permissions": [
"reviews:summary:publish"
ddiff --git a/manifests/capabilities/tips.checkout.create.json b/manifests/capabilities/tips.checkout.create.json
iindex 56c020c3..c0609f44 100644
---- a/manifests/capabilities/tips.checkout.create.json
++++ b/manifests/capabilities/tips.checkout.create.json
@@@ -4,6 +4,7 @@
"owner": "tips",
"status": "active",
"visibility": "first-party",
++ "sensitive": true,
"description": "Start a tip for a supporter (on_behalf SubjectRef): paid from the supporter's Billing credit (billing.transfer.create) or through a Billing checkout intent (billing.intent.create). Tips never prices money; Billing does. Idempotent by Idempotency-Key.",
"permissions": [
"tips:checkout"
ddiff --git a/manifests/capabilities/tips.superchat.create.json b/manifests/capabilities/tips.superchat.create.json
iindex bd3b7091..5de9b940 100644
---- a/manifests/capabilities/tips.superchat.create.json
++++ b/manifests/capabilities/tips.superchat.create.json
@@@ -4,6 +4,7 @@
"owner": "tips",
"status": "active",
"visibility": "first-party",
++ "sensitive": true,
"description": "Start a paid (highlighted) chat message for a supporter; the paid_messages row exists only after Billing settles it.",
"permissions": [
"tips:checkout"
ddiff --git a/manifests/capabilities/wiki.revision.publish.json b/manifests/capabilities/wiki.revision.publish.json
iindex 2bcb96a3..00d3cc7c 100644
---- a/manifests/capabilities/wiki.revision.publish.json
++++ b/manifests/capabilities/wiki.revision.publish.json
@@@ -4,6 +4,7 @@
"owner": "wiki",
"status": "active",
"visibility": "first-party",
++ "sensitive": true,
"description": "Publish a revision now or at a scheduled time (idempotent), unpublish a page, and record a person's review (approved or rejected) of an AI proposal or of an existing AI-assisted revision, on behalf of the editor named in X-OV-Subject. AI-generated revisions cannot be published without an approving review by a person.",
"permissions": [
"wiki:revision:publish"
ddiff --git a/package.json b/package.json
iindex fa5afecf..cf252553 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.76.0",
++ "version": "0.77.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",