Published in openvibe-contracts v0.97.0 (docs/adr/ADR-048-services-control-plane.md), rendered as is.

ADR-048: The Services control plane — authority aggregation, OVRN and one control-operation contract

Status: Accepted 2026-10-04 (plan track T13, step 1). Builds on ADR-034 §2 (one resource name) and §5 (control plane and data plane) and on ADR-046 §6 (the data plane keeps running without the control plane). Contracts: common.resource-name@1, common.resource-control-request@1, common.resource-control-result@1; helpers in contracts.resources (lib/resources.js).

Evidence

Decision

  1. Services aggregates authorities and owns no other service's rows. It reads each authority's resource index and events and keeps at most a read model it can rebuild from them. It never opens, writes or migrates another service's database, and its outage leaves every authority serving its own API.
  2. Every control operation is a call to the owning authority. Create, update, delete, start, stop, suspend, resume, resize, rotate, pair, grant, revoke and archive are sent as a common.resource-control-request@1 to the control API of the service named by the resource, and answered with a common.resource-control-result@1. The authority checks the caller's grant, decides, applies and emits its own events; Services only shows the answer. Actor's Console and any other operator surface use the same contract.
  3. OVRN is the one cross-service resource name: ovrn:<service>:<project_id>:<type>/<id> (common.resource-name@1), for example ovrn:media:prj_01J…:object/med_01K….

Authority boundaries

| Concern | The authority (owning service) | OpenVibe.Services | |---|---|---| | Rows and bytes | owns, writes, migrates, exports and erases them | never holds them; at most a rebuildable read model | | Grants and policy | checks the caller's capability and the on_behalf_of subject's grant on every control call | asks; never decides for an authority | | Sensitive-action gate | decides which actions need confirmation and refuses until one is approved | shows the confirmation, collects the owner's approval, retries | | Events and audit | emits the resource's events and audit rows | consumes them for its index and activity views | | Resource index | answers GET /api/v1/resources with common.resource-summary@1 | fans out, merges and paginates | | Projects, apps, keys, nodes | OpenVibe.Network | shows and calls Network's control API like any other authority |

The project segment of every OVRN is the tenancy boundary: a control request's resource must be a resource of its project_id, and contracts.resources.checkControlRequest refuses one that is not.

Idempotency and confirmations

confirmation_required appears only on refused, and done and pending carry no problem. contracts.resources.checkControlResult enforces these rules.

Out of scope

Consequences