openvibers/openvibe.contracts Commits

Contracts 0.107.0: resource-index capabilities for media, events, host and codes (T13 step 8); Host is live. (#54)

OP OpenVibers committed 7 Oct 2026 · 3ae96eff32 · 40 files, +203 −69

openvibe-contracts 0.107.0: the resource-index capabilities for plan T13 step 8, and Host is live.

- manifests/capabilities/{media,events,host,codes}.resource.read.json (planned, first-party, implementedBy [] until each authority serves GET /api/v1/resources): media.object (med_); events.subscription (sub_); host.site (sit_), host.deploy (dpl_), host.domain (dom_); codes.manifest (mfs_), codes.release (rel_). Each service manifest lists its capability.
- Decisions recorded in ADR-048: only Network lists projects (every other summary carries project_id); Media lists objects alone (VODs and clips are projections with bigint ids); events.queue stays out until Events stores a queue row (catalog row marked future); codes.repo becomes codes.manifest/codes.release (Codes hosts no repositories); person-owned resources (Bot robots/devices, Chat rooms, Community spaces, OpenRe streams, Games characters) are step 8 phase 2, served like Network user-owned node principals (no OVRN).
- lib/ids.js PREFIX records sub, dpl, dom, mfs, rel, sit; test/resources.test.js proves host.site/sit_ composes an OVRN (Host renamed site_ to sit_ in Host#27 with zero sites in production).
- manifests/services/host.json exposure: live / service (Stage B launched at openvibe.host on 2026-10-07).
- version 0.107.0, CHANGELOG, generated/ (version stamps).

Tests: node test/run.js: 17015 checks passed; compat vs v0.106.0: no breaking changes; generate --check up to date.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex b699633d..3d920454 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -4,6 +4,47 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
++## 0.107.0 — 2026-10-07
++
++**OpenVibe.Host is live at openvibe.host** (Stage B launched 2026-10-07; Host#26, Sites#11): `manifests/services/host.json` `exposure` becomes `{ state: live, publicSite: service }`, so Network lists Host as an open site.
++
++**The resource index's first authorities** (ADR-048, plan T13 step 8, the Contracts half). OpenVibe.Network has served
++`GET /api/v1/resources` since 0.104.0; this adds the planned, first-party capability each surveyed authority needs to
++serve its own — `media.resource.read`, `events.resource.read`, `host.resource.read` and `codes.resource.read` — and
++names in each the kinds that authority's index will carry. Additive and inert: four new capability manifests, four
++service capability lists extended, six id prefixes recorded in `lib/ids.js`; no schema changed, no route named, nothing
++activated.
++
++- `media.resource.read` (owner `media`, first-party, **planned**, no routes): Media's index carries its objects, kind
++ `media.object` with a `med_<ULID>` id — the canonical blobs its v1 vods, clips and files are projections of
++ (`media_objects`, migrations/0001_initial.sql). Vods and clips are projections over objects (bigint ids) and are
++ never listed. An object in a developer-project tenant carries that project; an object in a first-party app tenant
++ (`live`, `games`, …) has no project and so no OVRN.
++- `events.resource.read` (owner `events`, first-party, **planned**, no routes): Events' index carries its
++ subscriptions, kind `events.subscription` with a `sub_<ULID>` id (`server/api/subscriptions.js`), project-scoped
++ when a developer app created them and project-less when a service did. Events themselves (`evt_`) are what
++ `common.resource-name@1` refuses by design, and a project's queue stays out of the index until Events stores a queue
++ row.
++- `host.resource.read` (owner `host`, first-party, **planned**, no routes): Host's index carries its static sites
++ (kind `host.site`, `sit_<ULID>`), the immutable deploys of those sites (kind `host.deploy`, `dpl_<ULID>`) and their
++ default and custom domains (kind `host.domain`, `dom_<ULID>`), each with its `project_id`. Host renamed its site id
++ prefix from `site_` (four letters) to `sit_` (three) — zero sites exist in production, so nothing converts — so
++ `host.site` now composes an OVRN. Host lists no projects: only Network lists projects.
++- `codes.resource.read` (owner `codes`, first-party, **planned**, no routes): Codes' index carries its validated app
++ and mod manifests (`codes.manifest`, `mfs_<ULID>`) and its releases (`codes.release`, `rel_<ULID>`), each created
++ in a Network project. Trust tiers are per-app metadata and playground run logs are audit records, neither a
++ resource; Codes hosts no repositories, so the ADR-048 catalog's `codes.repo` is gone.
++- **Step 8 phase 2 — the person-owned resources** (Bot robots and devices, Chat rooms, Community spaces,
++ OpenRe.Stream streams and Games characters): served like Network's user-owned node principals, no OVRN and
++ owner = user, in a later phase, so no `*.resource.read` capability yet.
++- **Surveyed and left out** (not even a person-owned resource, so no `*.resource.read` capability): AI (runs and the
++ record groups are keyed to requester principals and attribution subjects, never a project) and Tools (a `job_<ULID>`
++ is exactly what `common.resource-name@1` refuses, and a job expires unless referenced).
++- **`lib/ids.js` records the chosen prefixes** (`subscription: sub`, `deploy: dpl`, `domain: dom`, `manifest: mfs`,
++ `release: rel`, `site: sit`); `lib/resources.js` still formats any `<service>.<type>` kind generically, so `nameOf`
++ needs no new entry, and `common.resource-name@1` is unchanged. `generated/` gains nothing but the release's version
++ stamp — only active capabilities with routes are emitted, and these are planned with none.
++
## 0.106.0 — 2026-10-07
**OpenVibe.Bot is a site** (Bot #34). Bot has served openvibe.bot since 2026-10-02 (`exposure` live, `publicSite`
ddiff --git a/docs/adr/ADR-048-services-control-plane.md b/docs/adr/ADR-048-services-control-plane.md
iindex 01ff6d0a..9b22750a 100644
---- a/docs/adr/ADR-048-services-control-plane.md
++++ b/docs/adr/ADR-048-services-control-plane.md
@@@ -3,7 +3,10 @@
**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.
++id prefixes Events and Codes still owe, plus the prefixes not yet in `lib/ids.js`. Amended 2026-10-07 (step 8, the
++Contracts half): only Network lists projects, Media lists objects alone, Events waits for a stored queue row, Codes
++hosts no repositories, the person-owned kinds are step 8 phase 2, and the chosen prefixes joined `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
@@@ -56,7 +59,8 @@ and data plane) and on ADR-046 §6 (the data plane keeps running without the con
| 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.
++`project_id`, and `contracts.resources.checkControlRequest` refuses one that is not. Authorities other than Network
++never list projects; every summary carries `project_id`.
**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
@@@ -77,22 +81,28 @@ The move has consumers outside `server/registry/offers.js` itself, which the Net
- 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:
++same three-letter prefix its OVRN uses (`lib/ids.js`). The catalog the step-8 index sweep starts from; every prefix
++below is in `lib/ids.js` except `act`, `run` and `zon`, which 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** |
++| Codes | `codes.manifest` | `mfs` |
++| Codes | `codes.release` | `rel` |
++| Events | `events.queue` | **future: when Events stores queues** |
++| Events | `events.subscription` | `sub` |
| 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
++Codes hosts no repositories, so `codes.repo` is gone. Media's index lists only its objects; its v1 vods and clips are
++projections over objects (bigint ids) and are never listed. The person-owned resources — Bot robots and devices, Chat
++rooms, Community spaces, OpenRe.Stream streams and Games characters — are step 8 phase 2, served like Network's
++user-owned node principals: no OVRN, owner = user.
++
++Events' `queue` is future until Events stores a queue row; `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.
@@@ -126,7 +136,7 @@ every missing prefix, resolves the `run_` collision and adds them to `lib/ids.js
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`.
--- The three-letter id prefixes of `events.queue`, `events.subscription` and `codes.repo` (unchosen) and the
++- The three-letter id prefix of `events.queue` (unchosen until Events stores queues) 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).
ddiff --git a/generated/openapi/ai.json b/generated/openapi/ai.json
iindex dac05438..e6d77f9c 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.106.0",
-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.107.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/billing.json b/generated/openapi/billing.json
iindex f6466b74..9a99ebc9 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.106.0",
-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.107.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/blog.json b/generated/openapi/blog.json
iindex 3f1b55a8..193d8fbe 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.106.0",
-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.107.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/bot.json b/generated/openapi/bot.json
iindex 73fd721b..6e6697e8 100644
---- a/generated/openapi/bot.json
++++ b/generated/openapi/bot.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Bot API",
-- "version": "0.106.0",
-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 600c6f49..e5b32740 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.106.0",
-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 86722fec..cfecdf36 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.106.0",
-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.107.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/community.json b/generated/openapi/community.json
iindex ab9fa03d..c6a6f26e 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.106.0",
-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 a19d6421..9aa2107f 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.106.0",
-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 259dc2fb..9ee2668b 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.106.0",
-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 a39f0e44..761e44f3 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.106.0",
-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.107.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/games.json b/generated/openapi/games.json
iindex c2e68324..80eac0cc 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.106.0",
-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.107.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/host.json b/generated/openapi/host.json
iindex 59f9f91a..8d283dc4 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.106.0",
-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 3d010e9f..a03bdeca 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.106.0",
-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 3249a5f2..88605639 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.106.0",
-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 6da2b322..c5890508 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.106.0",
-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.107.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/news.json b/generated/openapi/news.json
iindex 182fdf22..4b24419f 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.106.0",
-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 455f87e3..73161cd3 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.106.0",
-- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.107.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/reviews.json b/generated/openapi/reviews.json
iindex 28316951..d3936137 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.106.0",
-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 99de274e..a3726862 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.106.0",
-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 1b12ef5e..af0f191e 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.106.0",
-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.107.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/space.json b/generated/openapi/space.json
iindex 073d3419..f2b4709b 100644
---- a/generated/openapi/space.json
++++ b/generated/openapi/space.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Space API",
-- "version": "0.106.0",
-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 3063b3ba..e0493b35 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.106.0",
-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.107.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/tools.json b/generated/openapi/tools.json
iindex 2e5650d4..7b536687 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.106.0",
-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 d3ace023..4e11d68f 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.106.0",
-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.107.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 11dd6e23..d065c2ee 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.106.0",
-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.107.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/wiki.json b/generated/openapi/wiki.json
iindex f44d162d..4f2c7017 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.106.0",
-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.106.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.107.0",
++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.107.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/lib/ids.js b/lib/ids.js
iindex c10b8e64..5b66881a 100644
---- a/lib/ids.js
++++ b/lib/ids.js
@@@ -6,7 +6,7 @@
const crypto = require('crypto');
const ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
--const PREFIX = { user: 'usr', guest: 'gst', app: 'app', mod: 'mod', agent: 'agt', node: 'nod', event: 'evt', media: 'med', project: 'prj', confirmation: 'cnf', job: 'job', task: 'tsk', watch: 'wch', observation: 'wco', check: 'ckr' };
++const PREFIX = { user: 'usr', guest: 'gst', app: 'app', mod: 'mod', agent: 'agt', node: 'nod', event: 'evt', media: 'med', project: 'prj', confirmation: 'cnf', job: 'job', task: 'tsk', watch: 'wch', observation: 'wco', check: 'ckr', subscription: 'sub', deploy: 'dpl', domain: 'dom', manifest: 'mfs', release: 'rel', site: 'sit' };
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}$/;
ddiff --git a/manifests/capabilities/codes.resource.read.json b/manifests/capabilities/codes.resource.read.json
nnew file mode 100644
iindex 00000000..d30e2e4c
---- /dev/null
++++ b/manifests/capabilities/codes.resource.read.json
@@@ -0,0 +1,19 @@
++{
++ "id": "codes.resource.read",
++ "version": "1.0",
++ "owner": "codes",
++ "status": "planned",
++ "visibility": "first-party",
++ "description": "Read OpenVibe.Codes' resource index (ADR-048): GET /api/v1/resources lists the resources Codes owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Codes' own resources are its validated app and mod manifests (kind codes.manifest, id mfs_<ULID>) and its releases (kind codes.release, id rel_<ULID>), each carrying the Network project it was created in as its tenancy boundary. Trust tiers are per-app metadata and playground run logs are audit records, neither a resource; Codes hosts no repositories, so the catalog carries no codes.repo kind. Planned until Codes ships the index.",
++ "permissions": [
++ "resources:read"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "standard",
++ "events": [],
++ "implementedBy": [],
++ "inputSchema": "common.no-body@1",
++ "outputSchema": "common.resource-list-result@1"
++}
ddiff --git a/manifests/capabilities/events.resource.read.json b/manifests/capabilities/events.resource.read.json
nnew file mode 100644
iindex 00000000..c6f453e9
---- /dev/null
++++ b/manifests/capabilities/events.resource.read.json
@@@ -0,0 +1,19 @@
++{
++ "id": "events.resource.read",
++ "version": "1.0",
++ "owner": "events",
++ "status": "planned",
++ "visibility": "first-party",
++ "description": "Read OpenVibe.Events' resource index (ADR-048): GET /api/v1/resources lists the resources Events owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Events' own resources are its subscriptions (kind events.subscription, id sub_<ULID>), project-scoped when a developer app created them (project_id, with its environment) and project-less when a service did. An event is not a resource — evt_<ULID> is exactly what common.resource-name@1 refuses by design — and a project's queue is a derived carrier class, not a stored row, so it stays out of the index until Events stores a queue row. Planned until Events ships the index.",
++ "permissions": [
++ "resources:read"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "standard",
++ "events": [],
++ "implementedBy": [],
++ "inputSchema": "common.no-body@1",
++ "outputSchema": "common.resource-list-result@1"
++}
ddiff --git a/manifests/capabilities/host.resource.read.json b/manifests/capabilities/host.resource.read.json
nnew file mode 100644
iindex 00000000..b8d94d45
---- /dev/null
++++ b/manifests/capabilities/host.resource.read.json
@@@ -0,0 +1,19 @@
++{
++ "id": "host.resource.read",
++ "version": "1.0",
++ "owner": "host",
++ "status": "planned",
++ "visibility": "first-party",
++ "description": "Read OpenVibe.Host's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Host's own resources are its static sites (kind host.site, id sit_<ULID>), the immutable deploys of those sites (kind host.deploy, id dpl_<ULID>) and their default and custom domains (kind host.domain, id dom_<ULID>), each with the project_id of the Network project that owns it. Host lists no projects; only Network lists projects. Planned until Host ships the index.",
++ "permissions": [
++ "resources:read"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "standard",
++ "events": [],
++ "implementedBy": [],
++ "inputSchema": "common.no-body@1",
++ "outputSchema": "common.resource-list-result@1"
++}
ddiff --git a/manifests/capabilities/media.resource.read.json b/manifests/capabilities/media.resource.read.json
nnew file mode 100644
iindex 00000000..2003502a
---- /dev/null
++++ b/manifests/capabilities/media.resource.read.json
@@@ -0,0 +1,19 @@
++{
++ "id": "media.resource.read",
++ "version": "1.0",
++ "owner": "media",
++ "status": "planned",
++ "visibility": "first-party",
++ "description": "Read OpenVibe.Media's resource index (ADR-048): GET /api/v1/resources lists the resources Media owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Media's own resources are its objects (kind media.object, id med_<ULID>), the canonical blobs its v1 vods, clips and files are projections of (migrations/0001_initial.sql media_objects); vods and clips are projections over objects (bigint ids) and are never listed. An object carries the project of the tenant that owns it when that tenant is a developer project; an object in a first-party app tenant has no project and so no OVRN. Planned until Media ships the index.",
++ "permissions": [
++ "resources:read"
++ ],
++ "resourceConstraints": [
++ "none"
++ ],
++ "quotaClass": "standard",
++ "events": [],
++ "implementedBy": [],
++ "inputSchema": "common.no-body@1",
++ "outputSchema": "common.resource-list-result@1"
++}
ddiff --git a/manifests/services/codes.json b/manifests/services/codes.json
iindex 67bf7cb2..e25ce271 100644
---- a/manifests/services/codes.json
++++ b/manifests/services/codes.json
@@@ -27,6 +27,7 @@
"position": 7
},
"capabilities": [
++ "codes.resource.read",
"codes.release.read",
"codes.release.manage"
],
ddiff --git a/manifests/services/events.json b/manifests/services/events.json
iindex dfefb056..f38207f4 100644
---- a/manifests/services/events.json
++++ b/manifests/services/events.json
@@@ -21,6 +21,7 @@
"capabilities": [
"events.event.publish",
"events.event.read",
++ "events.resource.read",
"events.subscription.manage",
"events.delivery.admin",
"events.app.publish",
ddiff --git a/manifests/services/host.json b/manifests/services/host.json
iindex 7e29ee2f..57452f01 100644
---- a/manifests/services/host.json
++++ b/manifests/services/host.json
@@@ -15,9 +15,8 @@
"ready": "/api/ready",
"internalOrigin": "http://127.0.0.1:4910",
"exposure": {
-- "state": "internal",
-- "publicSite": "placeholder",
-- "note": "the domain serves the OpenVibe.Sites placeholder page"
++ "state": "live",
++ "publicSite": "service"
},
"site": {
"name": "Host",
@@@ -69,6 +68,7 @@
"host.site.manage",
"host.deploy.create",
"host.domain.manage",
++ "host.resource.read",
"host.site.config"
],
"eventsProduced": [
ddiff --git a/manifests/services/media.json b/manifests/services/media.json
iindex 76243977..f7ea2a4e 100644
---- a/manifests/services/media.json
++++ b/manifests/services/media.json
@@@ -36,6 +36,7 @@
"media.object.list",
"media.object.read",
"media.object.upload",
++ "media.resource.read",
"media.upload.create"
],
"eventsProduced": [
ddiff --git a/package-lock.json b/package-lock.json
iindex 31646da6..d19e1af9 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-contracts",
-- "version": "0.106.0",
++ "version": "0.107.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-contracts",
-- "version": "0.106.0",
++ "version": "0.107.0",
"license": "AGPL-3.0-only",
"dependencies": {
"ajv": "^8.17.1",
ddiff --git a/package.json b/package.json
iindex 26a0fe3a..1fa62388 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.106.0",
++ "version": "0.107.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",
ddiff --git a/test/resources.test.js b/test/resources.test.js
iindex 25c9f942..f2fce0db 100644
---- a/test/resources.test.js
++++ b/test/resources.test.js
@@@ -28,6 +28,10 @@ assert.strictEqual(c.resources.nameOf(summary), 'ovrn:watch:prj_01K6R2Z8C4V7M9Q3
assert.strictEqual(c.resources.nameOf({ ...summary, service: 'media' }), null, 'a kind of another service names nothing');
assert.strictEqual(c.resources.nameOf({ ...summary, project_id: undefined }), null, 'a summary without a project names nothing');
++// Host renamed its site id prefix from site_ (four letters) to sit_ (three), so host.site now composes an OVRN.
++const site = { ...summary, id: 'sit_01K6R3A1B2C3D4E5F6G7H8J9KA', kind: 'host.site', service: 'host' };
++assert.strictEqual(c.resources.nameOf(site), 'ovrn:host:prj_01K6R2Z8C4V7M9Q3T5W1X2Y3Z4:site/sit_01K6R3A1B2C3D4E5F6G7H8J9KA');
++
// Control requests: the schema passes these, the cross-field rules refuse them.
for (const f of ['other-project.json', 'create-with-resource.json']) {
const q = fx('common.resource-control-request', 'invalid', f);