openvibers/openvibe.contracts Commits

ADR-048 amendment: the resource index owns /api/v1/resources, Network's Fabric offers move to /api/v1/offers; resource-kind catalog with the prefixes still owed (#44)

OP OpenVibers committed 5 Oct 2026 · 810c6400a1 · 1 file, +49 −3

* ADR-048 amendment: the resource index owns /api/v1/resources, Network's Fabric offers move to /api/v1/offers; resource-kind catalog with the prefixes still owed

**Changes (docs/adr/ADR-048-services-control-plane.md)**
- `:3-7` — Status: added the 2026-10-05 amendment note (index owns `/api/v1/resources`; Network's T2 offers move public routes to `/api/v1/offers`; catalog records owed prefixes; no schema changes).
- `:53` — Authority boundaries table, Resource index row: now states the index **owns** `GET /api/v1/resources` and no other public catalog mounts that path.
- `:54` — new table row: OpenVibe.Network's T2 Fabric offer registry (`server/registry/offers.js`), public at `GET /api/v1/offers` (detail `/:offer_id`, beacon `/:offer_id/beacon`), report `POST /internal/resources/report`.
- `:60-66` — paragraph: the offers move, internal report/reads and the `network.resource.report` guard unchanged, and that this public-route change needs a Network release note naming old and new paths.
- `:68-84` — resource-kind catalog table (`actor.actor` `act`, `codes.repo` unchosen, `events.queue` unchosen, `events.subscription` unchosen, `media.object` `med`, `run.sandbox` `run`, `watch.watch` `wch`, `zone.object-zone` `zon`) with the note that step 8 chooses the three missing prefixes in `lib/ids.js`; explicit "no contract schema carries this catalog".
- `:115-116` — Out of scope bullet now names `events.queue`, `events.subscription`, `codes.repo` and points at the catalog.

**Where the referenced shape differed**
- The local checkout's `main` (5781a6b) is stale: T2 is design-only there (`docs/t2-resource-registry.md` says "Nothing … is built yet"; no `server/registry/offers.js`, no migration 0007). I read **`origin/main` (1311728)**, where it is built.
- On `origin/main`, `server/index.js:484-485` is exactly `app.use('/api/v1/resources', offerRouters.pub)` and `app.use('/internal/resources', offerRouters.internal)`; the `require('./registry/offers')` is line 483, and the report route is `POST /internal/resources/report`. The task's 484-485 citation matched the two mount lines.
- `docs/t2-resource-registry.md:129-138` did **not** match: that range is validation/write-pass prose. The routing paragraph is at `:140-141` and the public read API (list, detail, beacon) table at `:148-151`. I cited the doc without line numbers to avoid drift.

**Test results**
- `node scripts/docs-currency.js ~/OpenVibers` → this repo `✓ contracts current`; overall 13/36, exit 1, every gap pre-existing cross-repo `STATUS.json`/`package.json` drift unrelated to this change.
- `node scripts/docs-currency.js ~/OpenVibers --only contracts` → `✓ contracts current`, `1/1 current`, exit 0.
- No contract/schema/manifest files touched (`git status` shows only the ADR), so no `ov test` file is reached; none was run per the machine rule.

* ADR-048: the offers move is a coming Network release, and act/run/zon are proposed prefixes, not chosen (review)

Both findings fixed in `docs/adr/ADR-048-services-control-plane.md` (docs-only; no commit/push).

1. **Offers route move no longer stated as done** — status note now says the offers are *currently* at `/api/v1/resources` and move to `/api/v1/offers` in Network's release (`:3-5`); the Authority-boundaries row (`:55`) and the paragraph (`:60-70`) split current paths from the release-time paths, and the "no other public catalog mounts that path" claim is now qualified as not-yet-true (`:66-67`). The consumers the review names are listed (`:72-77`): discovery index key `resources: '/api/v1/resources'` at `server/registry/ecosystem.js:329` (doc `:31`), `docs/t2-resource-registry.md:136-138,254`, and `docs/cutover-t14-user-owned-trust.md:158`.

2. **Id-prefix catalog marks the unchosen prefixes** — `act`, `run`, `zon` are now each labelled `(proposed, not in lib/ids.js)` in the table (`:85,90,92`), the prose says only `med`/`wch` exist in `lib/ids.js` and calls out the `run_` collision with AI run ids (`contracts/ai/run.v1.json`) (`:80-83,94-98`), and the Out-of-scope bullet was updated to match (`:129-131`).

Test results: `node scripts/docs-currency.js ~/OpenVibers --only contracts` → `✓ contracts current`, `1/1 current`, exit 0. No contract/schema/test file is reached by a docs-only change, so no `ov test` file applies and none was run.

Changes

ddiff --git a/docs/adr/ADR-048-services-control-plane.md b/docs/adr/ADR-048-services-control-plane.md
iindex 5ff8bd8d..01ff6d0a 100644
---- a/docs/adr/ADR-048-services-control-plane.md
++++ b/docs/adr/ADR-048-services-control-plane.md
@@@ -1,6 +1,10 @@
# 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
++**Status:** Accepted 2026-10-04 (plan track T13, step 1). Amended 2026-10-05: the resource index owns
++`GET /api/v1/resources` and OpenVibe.Network's T2 Fabric offer registry, whose public routes are currently at
++`/api/v1/resources`, moves them to `/api/v1/offers` in Network's release; the resource-kind catalog below records the
++id prefixes Events and Codes still owe, plus the prefixes not yet in `lib/ids.js`. No contract schema changes.
++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`).
@@@ -47,12 +51,52 @@ and data plane) and on ADR-046 §6 (the data plane keeps running without the con
| 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 |
++| Resource index | owns `GET /api/v1/resources`, answering `common.resource-summary@1` for every resource it holds; no other public catalog is to mount that path once Network's offers move to `/api/v1/offers` (today they still share `/api/v1/resources`) | fans out, merges and paginates |
++| Resource offers (T2 Fabric) | OpenVibe.Network's offer registry (`server/registry/offers.js`), currently public at `GET /api/v1/resources` and moving to `GET /api/v1/offers` (`/:offer_id` for detail, `/:offer_id/beacon` for the probe) in Network's release; reported at `POST /internal/resources/report` | shows and calls Network's control API like any other authority |
| 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.
++**The index owns `/api/v1/resources`; the offers move to `/api/v1/offers` in Network's release.** That path belongs
++to the resource index above, so OpenVibe.Network's T2 Fabric offer registry (`server/registry/offers.js`, mounted in
++`server/index.js:484-485`, designed in `docs/t2-resource-registry.md`) **currently serves its public routes at**
++`GET /api/v1/resources`, `GET /api/v1/resources/:offer_id` and `GET /api/v1/resources/:offer_id/beacon`, and **moves
++them in a Network release** to `GET /api/v1/offers`, `GET /api/v1/offers/:offer_id` and
++`GET /api/v1/offers/:offer_id/beacon`. Until that release lands, the claim that no other public catalog mounts
++`/api/v1/resources` is not yet true — Network's offers still share the path. The machine report keeps
++`POST /internal/resources/report`, and the internal reads keep their `/internal/resources` paths and the
++`network.resource.report` guard. Moving a public route is a release-note change: OpenVibe.Network's release must name
++the old and new paths.
++
++The move has consumers outside `server/registry/offers.js` itself, which the Network release note must update with it:
++
++- the **discovery index key** `resources: '/api/v1/resources'` in `server/registry/ecosystem.js:329` (documented at
++ `:31`);
++- the **public read API tables and worked examples** in `docs/t2-resource-registry.md:136-138,254`; and
++- the **cutover runbook probe** in `docs/cutover-t14-user-owned-trust.md:158`.
++
++**Resource kinds and three-letter id prefixes.** Every summary's `kind` is `<service>.<type>` and its id carries the
++same three-letter prefix its OVRN uses (`lib/ids.js`). The catalog the step-8 index sweep starts from; only `med` and
++`wch` are in `lib/ids.js` today, the rest are proposed and not yet chosen there:
++
++| Service | Kind | Id prefix |
++|---|---|---|
++| Actor | `actor.actor` | `act` (proposed, not in `lib/ids.js`) |
++| Codes | `codes.repo` | **unchosen** |
++| Events | `events.queue` | **unchosen** |
++| Events | `events.subscription` | **unchosen** |
++| Media | `media.object` | `med` |
++| Run | `run.sandbox` | `run` (proposed, not in `lib/ids.js`; collides with the existing `run_` AI run ids, `contracts/ai/run.v1.json`) |
++| Watch | `watch.watch` | `wch` |
++| Zone | `zone.object-zone` | `zon` (proposed, not in `lib/ids.js`; today only a usage-recorded description field `<zon_id>`) |
++
++Events' `queue` and `subscription` and Codes' `repo` have no three-letter prefix at all; `act`, `run` and `zon` are
++proposed but are not in `lib/ids.js` either. `run` cannot simply be added: `run_` is already OpenVibe.AI's run-id
++pattern (`contracts/ai/run.v1.json`), so a `run.sandbox` id would collide with an AI run id. The step-8 sweep chooses
++every missing prefix, resolves the `run_` collision and adds them to `lib/ids.js` before those services answer
++`GET /api/v1/resources`. No contract schema carries this catalog: it is prose, and recording it changes no schema.
++
## Idempotency and confirmations
- Every control request carries an `idempotency_key` (8–200 characters). The authority stores the first answer per
@@@ -82,7 +126,9 @@ The project segment of every OVRN is the tenancy boundary: a control request's `
redefined here.
- How a confirmation is approved (WS-Z2), billing of control calls, and moving `common.usage-recorded@1` `resource` to
a `$ref` of `common.resource-name@1`.
--- Three-letter id prefixes still to be chosen before the step-8 sweep (Events queues, Codes repositories).
++- The three-letter id prefixes of `events.queue`, `events.subscription` and `codes.repo` (unchosen) and the
++ proposed-but-not-in-`lib/ids.js` prefixes `act`, `run` and `zon`, including the `run_` collision with AI run ids, all
++ still to be settled before the step-8 sweep (the resource-kind catalog under Authority boundaries).
## Consequences