openvibers/openvibe.contracts Commits
Add ADR-046 fabric decision, user-owned trust rule and two products (#22)
Changes
@@@ -4,7 +4,26 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) thfrom `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,the notes were in the tag and commit messages (`git tag -n1`).--## Unreleased++## 0.88.0 — 2026-10-03++++**The Fabric ADR and the user-owned trust rule** (plan T1 step 3; T14 S2, the Contracts half). ADR-046 "The universal++adaptive fabric" (Proposed, for the owner) ties `platform.resource-offer@1`, `workload-requirements@1`,++`placement-result@1`, `placement-plan@1`, `provider-state@1`, `rate-card@1`, `cost-snapshot@1`, `telemetry-sample@1`++and `usage-sample@1` to one placer (`openvibe-sdk/placement`), names the five trust classes, the `worker:<class>`++capabilities a Node advertises (`worker:function`, `worker:code`, …) and the plane rule: the data plane keeps running++on the last valid signed plan (`signature`, `epoch`, `expires_at`, checked by `placement.verifyPlan`). It records that++`user-owned` is **not** in the default trust set: a user-owned offer is eligible only for a workload whose requirements++name `user-owned`, so the SDK's default trust list stays the other four (this replaces the 0.87.0 note that the SDK++should route to user-owned nodes by default). Whether a user-owned node may run other users' work, and its consent and++revocation rule, are open questions for the owner.++- `platform.resource-offer@1` `trust` and `platform.workload-requirements@1` `trust` gain descriptions stating that++ rule; the enums (1.1.0 since 0.87.0) are unchanged. Consumers are unchanged.++- Fixtures: `user-owned-node` now also advertises `worker:function`; new invalid `unknown-trust` (an offer with++ `trust: "stranger"`, refused on `/trust` alone). `owned-node` is unchanged.++++**Two product manifests** (plan T11 step 1): `manifests/products/openvibe.zone.json` (served by the planned `zone`++service) and `manifests/products/openvibe.work.json` (no repository yet), with name, tagline, pillars and launch taken++from OpenVibe.Sites `sites.json`. The catalog test's Sites snapshot holds 35 domains.- `network.project.read` is active, implemented by OpenVibe.Network `GET /internal/projects/:project_id`.@@@ -51,6 +51,7 @@},"trust": {"type": "string",++ "description": "the offer's trust class (ADR-046): first-party (operated by the OpenVibe network), user-owned (a person's own node, eligible only for workloads whose requirements name user-owned), partner, community or external","enum": ["first-party","user-owned",@@@ -85,7 +85,7 @@]},"minItems": 1,-- "description": "trust levels allowed to run it"++ "description": "trust classes allowed to run it (ADR-046); when absent, first-party, partner, community and external. user-owned is never a default: a user-owned offer is eligible only when this list names it"},"region": {"type": "string",nnew file mode 100644@@@ -0,0 +1,120 @@++# ADR-046: The universal adaptive fabric — offers, requirements, placement, trust classes and signed plans++++**Status:** Proposed 2026-10-03 (plan track T1 step 3, for the owner's review). Builds on ADR-034 §5 (control plane++and data plane; cells) and ADR-042 (the events fabric, the first placed workload class).++++## Context and current evidence++++- ADR-034 §5 splits the platform into a control plane (identity, projects, grants, registry, billing, deploy) and a++ data plane (Media bytes, Events streams, Chat rooms, Search indexes, AI runs), and scales the data plane by cells. It++ does not say how a piece of work is matched to the machine, provider or runtime that runs it.++- There is one placement implementation, `openvibe-sdk/placement`: `plan()` filters offers by hard constraints++ (`excluded()`), then ranks the rest by cost and latency with hysteresis, and explains the choice. `signPlan`,++ `verifyPlan` and `createPlanHolder` sign and check route plans. ADR-042 places event deliveries with it, and Media's++ fabric uses the same shape. No service is to write its own placer.++- The Fabric vocabulary already exists as 20 active `platform.*` contracts, all owned by `network`. What was missing++ was the trust class for a person's own machine (added in 0.87.0) and a written rule tying the contracts together.++++## Decision++++### 1. Offers, requirements, a result and a plan++++- **`platform.resource-offer@1`** — what a capacity holder offers: `kind` (`node`, `provider`, `storage`, `delivery`,++ `runtime`, `agent`, `harness`), region and cell, `trust`, `capabilities` (`<namespace>:<name>`), capacity, latency,++ `health`, `pricing`. Offers are facts reported by their holders; they never claim a placement.++- **`platform.workload-requirements@1`** — what a piece of work needs: `kind` (e.g. `ai.run`, `media.transcode`),++ `mobility` (`request`, `job`, `session`, `stateful-partition`), `latency_class`, `objective` (`cheapest`,++ `lowest-latency`, `balanced`, `private`, `local-only`, `first-party-only`, `high-reliability`, `correctness`), and++ optional limits: latency, deadline, cost, durability, residency, the allowed `trust` classes, region, capabilities.++- **`platform.placement-result@1`** — one decision: the `selected` offer, the objective, `reasons`, the ranked++ `candidates` and the `plan_epoch` it was made under. It is the explanation a person or an operator reads.++- **`platform.placement-plan@1`** — the control plane's routing table for the data plane: `routes` with their targets,++ a monotonically increasing `epoch`, `issued_at`, `expires_at`, `issuer`, `key_id` and an Ed25519 `signature` over the++ canonical JSON of every other field.++++Every placer is `openvibe-sdk/placement`. A service states requirements and reads results; it does not rank offers.++++### 2. Health and cost++++- **`platform.provider-state@1`** — a provider's usage in its billing period, forecast, reserve and health.++- **`platform.rate-card@1`** — a verified price: provider, metric, unit size and price, free allowance and its reset++ period, region, effective window, `source` and `verified_at`, so every price the placer uses names where it came++ from and when it was last checked.++- **`platform.cost-snapshot@1`** — actual cost per target over a window, with the counterfactual cost of the++ alternatives, so the placer's savings are measured, not assumed (ADR-034 §10).++++An offer whose health is neither `up` nor `degraded` is excluded before ranking (`excluded()`); provider state and rate cards then price++the remaining candidates (`marginalCost`, `forecast`).++++### 3. Telemetry and usage++++- **`platform.telemetry-sample@1`** — one observation with the universal dimensions (service, project, subject,++ resource, provider, node, cell, region, operation, latency, queue delay, TTFB, throughput, bytes, status, cache++ status, cost estimate, `route_epoch`, `trace_id`). It feeds offers' latency and health; it is never billing.++- **`platform.usage-sample@1`** — one idempotent unit of metered use (`id`, `idempotency_key`, quantity, unit,++ source), with the same dimensions plus free allowance used and vibes charged. It is the input to metering++ (ADR-034 §9). Both carry `route_epoch`, so any sample can be traced to the plan that routed it.++++### 4. Trust classes++++`resource-offer.trust` is one of five classes, in this order; `workload-requirements.trust` lists the classes allowed++to run the work:++++| Class | Meaning |++| --- | --- |++| `first-party` | Hosts and accounts the OpenVibe network operates itself (its own servers and provider accounts). |++| `user-owned` | A person's own node or machine (an OpenVibe.Node they run), offered for their own workloads. |++| `partner` | An organisation under a written agreement with the network, bound by its data and uptime terms. |++| `community` | Capacity volunteered by community members, without a contract; best effort, no private data. |++| `external` | A third-party provider used through its public API under its own terms (a cloud or AI vendor). |++++**`user-owned` is not in the default trust set.** When a requirement names no `trust`, the eligible classes are++`first-party`, `partner`, `community` and `external` (the SDK's `TRUST_ORDER`). A `user-owned` offer is eligible only++for a workload whose requirements list `"user-owned"` explicitly. Adding the class to the contracts therefore widens++no existing caller's eligible set, and the SDK must not add it to its default list.++++### 5. Worker capability names a Node advertises++++An OpenVibe.Node that runs jobs advertises one `worker:<class>` capability in its `resource-offer.capabilities` for++each `platform.runtime-class@1` class it runs: `worker:function` (a declared function artifact) and `worker:code`++(submitted code in a sandbox) today, and `worker:browser`, `worker:linux`, `worker:desktop` and `worker:gpu` as Node++gains them. These six names are reserved for the Node job worker (`$defs.reservedWorkerCapabilities` in++`platform.resource-offer@1`); every other `worker:` name (`worker:ffmpeg`, `worker:ai-gpu`) is an ordinary capability.++A requirement asks for a class by listing the same name in its `capabilities`.++++### 6. Control plane and data plane++++- The **control plane** collects offers, provider state and rate cards, runs the placer, and issues signed++ `placement-plan@1` documents, each with a higher `epoch` than the last.++- The **data plane** routes by the plan it holds. It accepts a new plan only when `openvibe-sdk/placement.verifyPlan`++ passes (known `key_id`, valid signature, `expires_at` in the future) and the plan's `epoch` is newer than the current++ one (`createPlanHolder`). A plan that fails is rejected and logged.++- **The data plane keeps running on the last valid signed plan.** A control-plane outage, a bad signature or a stale++ epoch never stops delivery; it only stops change. A plan past its `expires_at` is never accepted, and a data plane++ holding one keeps routing by it, reports itself stale, and does not invent routes of its own.++++### 7. The reference consumer++++T6 (OpenVibe.AI) is the reference consumer: it states `ai.run` requirements, places each run across first-party++nodes, external AI providers and, when the request names it, the person's own `user-owned` node, and records a++placement result, telemetry and usage samples for every run. Other services adopt the fabric after it.++++## Consequences++++- One vocabulary and one placer for every placed workload; a new kind of capacity is a new offer, not new code.++- Routing survives the control plane: plans are signed, epoch-ordered and verified where they are used.++- A person can run their own work on their own machine without that machine joining anyone else's default pool.++- The SDK placer, the T2 resource registry and OpenVibe.Node must agree on the trust rule; this ADR is that rule.++++## Open questions for the owner++++1. May a `user-owned` node run **other users'** workloads (as a community-style contributor), or only its owner's?++ Until answered, only its owner's, and only when the requirement names `user-owned`.++2. What is the consent and revocation rule for a `user-owned` node: how its owner opts each workload class in, how++ they withdraw (immediately, or after running jobs drain), and what the placer does with work in flight when consent++ is revoked?++++## What this ADR does not claim++++- No producer emits `user-owned` offers yet, and no service places across user-owned nodes today.++- Cells, the plan issuer and its key distribution are not built; ADR-034's phasing (roadmap WS-W) still applies.@@@ -40,3 +40,4 @@ Platform-wide decisions from the OpenVibe development roadmap (section 10). Each| [ADR-034](ADR-034-platform-north-star.md) | Platform north star, an open affordable cloud: boundaries now (projects, `ovrn:` resource names, scoped grants, generated contracts, metering), infrastructure when measured; control/data plane and cells; regions, edge nodes, load balancing and a geo API; pulled, gated deploys; four extension levels under one manifest | Proposed 2026-09-28, for the owner's review || [ADR-035](ADR-035-postgresql-and-valkey.md) | PostgreSQL 18 as every service's system of record (database + roles per service, PgBouncer transaction pooling, pgBackRest PITR to B2) and Valkey as the shared non-authoritative store; the SDK data layer; services migrate one at a time, smallest first; supersedes ADR-007's trigger rule; amendment: PostgreSQL dialect only (PGlite in tests), a verified SQLite importer, every service async and migrated | Accepted 2026-09-28 (owner decision), amended the same day || [ADR-042](ADR-042-events-fabric.md) | The events fabric: the delivery-policy class is the one vocabulary and TOPIC/QUEUE/STREAM a derived carrier class; PostgreSQL stays the record, carriers are transports that fail open to `pg-v1`; a delivery lease before any second worker; per-key ordering; the SDK planner places deliveries and explains them; JetStream for STREAM only; opaque cursors replace the global seq after one overlap release; hot/replay/archive tiers; amends ADR-004's NATS rejection | Accepted 2026-09-29 (plan T7) |++| [ADR-046](ADR-046-universal-adaptive-fabric.md) | The universal adaptive fabric: offers, requirements, placement results and signed plans through one placer (`openvibe-sdk/placement`); health, cost, telemetry and usage samples; five trust classes, with `user-owned` eligible only when a workload names it; `worker:<class>` Node capabilities; the data plane keeps running on the last valid signed plan; T6 AI the reference consumer | Proposed 2026-10-03 (plan T1 step 3), for the owner's review |nnew file mode 100644@@@ -0,0 +1,19 @@++{++ "offer_id": "off_stranger_1",++ "kind": "node",++ "node_id": "stranger-node-1",++ "provider": "self",++ "region": "home",++ "trust": "stranger",++ "capabilities": [++ "node:http",++ "worker:function"++ ],++ "health": {++ "status": "up"++ },++ "pricing": {++ "model": "free-allowance"++ },++ "updated_at": "2026-10-03T00:00:00Z"++}@@@ -7,6 +7,7 @@"trust": "user-owned","capabilities": ["node:http",++ "worker:function","worker:ai-gpu"],"capacity": {@@@ -58431,6 +58431,7 @@},"trust": {"type": "string",++ "description": "the offer's trust class (ADR-046): first-party (operated by the OpenVibe network), user-owned (a person's own node, eligible only for workloads whose requirements name user-owned), partner, community or external","enum": ["first-party","user-owned",@@@ -58888,7 +58889,7 @@]},"minItems": 1,-- "description": "trust levels allowed to run it"++ "description": "trust classes allowed to run it (ADR-046); when absent, first-party, partner, community and external. user-owned is never a default: a user-owned offer is eligible only when this list names it"},"region": {"type": "string",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.AI API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Billing API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Blog API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bot API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Chat API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Codes API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Community API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Coupons API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Deals API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Events API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Games API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Host API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Live API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Media API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Network API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.News API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenRe.Stream API",-- "version": "0.87.0",-- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Reviews API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Search API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Sources API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Space API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tips API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tools API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Trade API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.VIP API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Wiki API",-- "version": "0.87.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -50970,6 +50970,9 @@ export type ResourceOffer = {adapter?: string;region: string;cell?: string;++ /**++ * the offer's trust class (ADR-046): first-party (operated by the OpenVibe network), user-owned (a person's own node, eligible only for workloads whose requirements name user-owned), partner, community or external++ */trust: "first-party" | "user-owned" | "partner" | "community" | "external";/*** e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2. Kind harness: task:edit, task:review, task:browse, task:run, task:test and task:plan for the task kinds it takes (its detail's task_capabilities), and harness:mcp, harness:resume, harness:host-access and harness:long-autonomy for its detail's capabilities that are true. The `worker:<class>` names that map to platform.runtime-class@1 values are reserved for OpenVibe.Node's job worker, one per class: worker:function, worker:code, worker:browser, worker:linux, worker:desktop and worker:gpu ($defs.reservedWorkerCapabilities). A node advertises the name of each class it runs (Node's status.capabilities.worker); every other `worker:` name (worker:ffmpeg, worker:ai-gpu) is an ordinary capability name.@@@ -51074,7 +51077,7 @@ export interface WorkloadRequirements {*/residency?: string;/**-- * trust levels allowed to run it++ * trust classes allowed to run it (ADR-046); when absent, first-party, partner, community and external. user-owned is never a default: a user-owned offer is eligible only when this list names it** @minItems 1*/nnew file mode 100644@@@ -0,0 +1,39 @@++{++ "domain": "openvibe.work",++ "tld": "work",++ "name": "OpenVibe.Work",++ "icon": "fa-briefcase",++ "accent": "#34d399",++ "tagline": "Find work, with an agent on your side.",++ "description": "OpenVibe.Work will gather job listings from open sources with their provenance, let you search them and watch for new ones that fit, and let an agent help you prepare and apply — with your approval at every step. Part of the OpenVibe network, with one account for every site.",++ "pillars": [++ {++ "title": "Every listing sourced",++ "text": "Where it came from and when, always shown."++ },++ {++ "title": "Watch for fits",++ "text": "Get told when a job that matches appears."++ },++ {++ "title": "An agent that helps",++ "text": "Drafts, tailors and tracks applications — you approve."++ },++ {++ "title": "Yours to control",++ "text": "Your profile and history stay yours, exportable any time."++ }++ ],++ "keywords": "openvibe work, job search, job listings, job alerts, ai job application assistant",++ "launch": "job listings and alerts",++ "relationships": {++ "noRepo": true,++ "meanwhile": [++ {++ "label": "OpenVibe.Network",++ "url": "https://openvibe.network",++ "note": "One free account for every OpenVibe site, ready today."++ }++ ]++ }++}nnew file mode 100644@@@ -0,0 +1,40 @@++{++ "domain": "openvibe.zone",++ "tld": "zone",++ "name": "OpenVibe.Zone",++ "icon": "fa-box-archive",++ "accent": "#2dd4bf",++ "tagline": "Your data, in zones.",++ "description": "OpenVibe.Zone will keep your data and files in zones — S3-style buckets for persistent storage that every OpenVibe service and agent can use with your permission, also offered to developers through OpenVibe.Services. Part of the OpenVibe network, with one account for every site.",++ "pillars": [++ {++ "title": "An S3-style API",++ "text": "Buckets, objects and signed links that existing tools already speak."++ },++ {++ "title": "Durable storage",++ "text": "Files and data that stay put, with copies kept safe."++ },++ {++ "title": "Access you control",++ "text": "Decide which services, agents and people can read or write each zone."++ },++ {++ "title": "Used across the network",++ "text": "The same zones back your streams, media, agents and apps."++ }++ ],++ "keywords": "openvibe zone, object storage, s3 compatible storage, buckets, persistent storage, file storage api",++ "launch": "zones for data and files",++ "relationships": {++ "service": "zone",++ "plannedRepo": "OpenVibe.Zone",++ "meanwhile": [++ {++ "label": "OpenVibe.Network",++ "url": "https://openvibe.network",++ "note": "One free account for every OpenVibe site, ready today."++ }++ ]++ }++}@@@ -1,12 +1,12 @@{"name": "openvibe-contracts",-- "version": "0.87.0",++ "version": "0.88.0","lockfileVersion": 3,"requires": true,"packages": {"": {"name": "openvibe-contracts",-- "version": "0.87.0",++ "version": "0.88.0","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.87.0",++ "version": "0.88.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",@@@ -1,5 +1,5 @@{-- "_about": "The 33 domains of OpenVibe.Sites sites.json (origin/main c2fda6c, 2026-09-29), snapshotted for the catalog home test (plan T11 lane D). Each has exactly one home: a service manifest site with a tld, or manifests/products/<domain>.json.",++ "_about": "The 35 catalog domains of OpenVibe.Sites sites.json: the 33 of origin/main c2fda6c (2026-09-29), snapshotted for the catalog home test (plan T11 lane D), plus openvibe.work and openvibe.zone (plan T11 step 1). Each has exactly one home: a service manifest site with a tld, or manifests/products/<domain>.json.","source": "OpenVibers/OpenVibe.Sites@c2fda6c:sites.json","domains": ["openvibe.news",@@@ -34,6 +34,8 @@"openvibe.bot","openvibe.watch","openvibe.actor",-- "openvibe.events"++ "openvibe.events",++ "openvibe.work",++ "openvibe.zone"]}@@@ -84,4 +84,16 @@ checks++;assert.strictEqual(contracts.validate('platform.workload-requirements', { ...userRequirement, trust: ['other'] }).valid, false, 'an unknown requirement trust is refused');checks++;++// The fixtures (ADR-046): a user-owned node advertises the Node job worker's worker:function, and the++// unknown-trust offer is refused for its trust alone.++const fixture = (p) => require(`../fixtures/platform.resource-offer/${p}.json`);++const userNode = fixture('valid/user-owned-node');++assert.ok(userNode.trust === 'user-owned' && userNode.capabilities.includes('worker:function'), 'user-owned-node advertises worker:function');++checks++;++const unknownTrust = contracts.validate('platform.resource-offer', fixture('invalid/unknown-trust'));++assert.strictEqual(unknownTrust.valid, false, 'the unknown-trust fixture is refused');++checks++;++assert.ok(unknownTrust.errors.length && unknownTrust.errors.every(e => e.path === '/trust'), `unknown-trust fails on /trust only: ${JSON.stringify(unknownTrust.errors)}`);++checks++;++console.log(`platform fabric: ${checks} assertions passed`);@@@ -29,7 +29,7 @@ for (const row of products.catalog()) homes.set(row.domain, [...(homes.get(row.dfor (const [domain, list] of homes) ok(list.length === 1, `${domain} has one home, not ${list.join(', ')}`);const snapshot = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures/sites-domains.json'), 'utf8')).domains;--ok(snapshot.length === 33 && new Set(snapshot).size === 33, 'the Sites snapshot holds 33 distinct domains');++ok(snapshot.length === 35 && new Set(snapshot).size === 35, 'the Sites snapshot holds 35 distinct domains');for (const d of snapshot) ok((homes.get(d) || []).length === 1, `${d} (OpenVibe.Sites) has exactly one Contracts home: ${(homes.get(d) || ['none']).join(', ')}`);for (const p of products.manifests) {
@@ -4,7 +4,26 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
@@ -4,7 +4,26 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
the notes were in the tag and commit messages (`git tag -n1`).
-## Unreleased
+## 0.88.0 — 2026-10-03
+
+**The Fabric ADR and the user-owned trust rule** (plan T1 step 3; T14 S2, the Contracts half). ADR-046 "The universal
+adaptive fabric" (Proposed, for the owner) ties `platform.resource-offer@1`, `workload-requirements@1`,
+`placement-result@1`, `placement-plan@1`, `provider-state@1`, `rate-card@1`, `cost-snapshot@1`, `telemetry-sample@1`
+and `usage-sample@1` to one placer (`openvibe-sdk/placement`), names the five trust classes, the `worker:<class>`
+capabilities a Node advertises (`worker:function`, `worker:code`, …) and the plane rule: the data plane keeps running
+on the last valid signed plan (`signature`, `epoch`, `expires_at`, checked by `placement.verifyPlan`). It records that
+`user-owned` is **not** in the default trust set: a user-owned offer is eligible only for a workload whose requirements
+name `user-owned`, so the SDK's default trust list stays the other four (this replaces the 0.87.0 note that the SDK
+should route to user-owned nodes by default). Whether a user-owned node may run other users' work, and its consent and
+revocation rule, are open questions for the owner.
+- `platform.resource-offer@1` `trust` and `platform.workload-requirements@1` `trust` gain descriptions stating that
+ rule; the enums (1.1.0 since 0.87.0) are unchanged. Consumers are unchanged.
+- Fixtures: `user-owned-node` now also advertises `worker:function`; new invalid `unknown-trust` (an offer with
+ `trust: "stranger"`, refused on `/trust` alone). `owned-node` is unchanged.
+
+**Two product manifests** (plan T11 step 1): `manifests/products/openvibe.zone.json` (served by the planned `zone`
+service) and `manifests/products/openvibe.work.json` (no repository yet), with name, tagline, pillars and launch taken
+from OpenVibe.Sites `sites.json`. The catalog test's Sites snapshot holds 35 domains.
- `network.project.read` is active, implemented by OpenVibe.Network `GET /internal/projects/:project_id`.
- `network.project.read` is active, implemented by OpenVibe.Network `GET /internal/projects/:project_id`.
@@ -51,6 +51,7 @@
@@ -51,6 +51,7 @@
},
},
"trust": {
"trust": {
"type": "string",
"type": "string",
+ "description": "the offer's trust class (ADR-046): first-party (operated by the OpenVibe network), user-owned (a person's own node, eligible only for workloads whose requirements name user-owned), partner, community or external",
"enum": [
"enum": [
"first-party",
"first-party",
"user-owned",
"user-owned",
@@ -85,7 +85,7 @@
@@ -85,7 +85,7 @@
]
]
},
},
"minItems": 1,
"minItems": 1,
- "description": "trust levels allowed to run it"
+ "description": "trust classes allowed to run it (ADR-046); when absent, first-party, partner, community and external. user-owned is never a default: a user-owned offer is eligible only when this list names it"
},
},
"region": {
"region": {
"type": "string",
"type": "string",
new file mode 100644
new file mode 100644
@@ -0,0 +1,120 @@
@@ -0,0 +1,120 @@
+# ADR-046: The universal adaptive fabric — offers, requirements, placement, trust classes and signed plans
+
+**Status:** Proposed 2026-10-03 (plan track T1 step 3, for the owner's review). Builds on ADR-034 §5 (control plane
+and data plane; cells) and ADR-042 (the events fabric, the first placed workload class).
+
+## Context and current evidence
+
+- ADR-034 §5 splits the platform into a control plane (identity, projects, grants, registry, billing, deploy) and a
+ data plane (Media bytes, Events streams, Chat rooms, Search indexes, AI runs), and scales the data plane by cells. It
+ does not say how a piece of work is matched to the machine, provider or runtime that runs it.
+- There is one placement implementation, `openvibe-sdk/placement`: `plan()` filters offers by hard constraints
+ (`excluded()`), then ranks the rest by cost and latency with hysteresis, and explains the choice. `signPlan`,
+ `verifyPlan` and `createPlanHolder` sign and check route plans. ADR-042 places event deliveries with it, and Media's
+ fabric uses the same shape. No service is to write its own placer.
+- The Fabric vocabulary already exists as 20 active `platform.*` contracts, all owned by `network`. What was missing
+ was the trust class for a person's own machine (added in 0.87.0) and a written rule tying the contracts together.
+
+## Decision
+
+### 1. Offers, requirements, a result and a plan
+
+- **`platform.resource-offer@1`** — what a capacity holder offers: `kind` (`node`, `provider`, `storage`, `delivery`,
+ `runtime`, `agent`, `harness`), region and cell, `trust`, `capabilities` (`<namespace>:<name>`), capacity, latency,
+ `health`, `pricing`. Offers are facts reported by their holders; they never claim a placement.
+- **`platform.workload-requirements@1`** — what a piece of work needs: `kind` (e.g. `ai.run`, `media.transcode`),
+ `mobility` (`request`, `job`, `session`, `stateful-partition`), `latency_class`, `objective` (`cheapest`,
+ `lowest-latency`, `balanced`, `private`, `local-only`, `first-party-only`, `high-reliability`, `correctness`), and
+ optional limits: latency, deadline, cost, durability, residency, the allowed `trust` classes, region, capabilities.
+- **`platform.placement-result@1`** — one decision: the `selected` offer, the objective, `reasons`, the ranked
+ `candidates` and the `plan_epoch` it was made under. It is the explanation a person or an operator reads.
+- **`platform.placement-plan@1`** — the control plane's routing table for the data plane: `routes` with their targets,
+ a monotonically increasing `epoch`, `issued_at`, `expires_at`, `issuer`, `key_id` and an Ed25519 `signature` over the
+ canonical JSON of every other field.
+
+Every placer is `openvibe-sdk/placement`. A service states requirements and reads results; it does not rank offers.
+
+### 2. Health and cost
+
+- **`platform.provider-state@1`** — a provider's usage in its billing period, forecast, reserve and health.
+- **`platform.rate-card@1`** — a verified price: provider, metric, unit size and price, free allowance and its reset
+ period, region, effective window, `source` and `verified_at`, so every price the placer uses names where it came
+ from and when it was last checked.
+- **`platform.cost-snapshot@1`** — actual cost per target over a window, with the counterfactual cost of the
+ alternatives, so the placer's savings are measured, not assumed (ADR-034 §10).
+
+An offer whose health is neither `up` nor `degraded` is excluded before ranking (`excluded()`); provider state and rate cards then price
+the remaining candidates (`marginalCost`, `forecast`).
+
+### 3. Telemetry and usage
+
+- **`platform.telemetry-sample@1`** — one observation with the universal dimensions (service, project, subject,
+ resource, provider, node, cell, region, operation, latency, queue delay, TTFB, throughput, bytes, status, cache
+ status, cost estimate, `route_epoch`, `trace_id`). It feeds offers' latency and health; it is never billing.
+- **`platform.usage-sample@1`** — one idempotent unit of metered use (`id`, `idempotency_key`, quantity, unit,
+ source), with the same dimensions plus free allowance used and vibes charged. It is the input to metering
+ (ADR-034 §9). Both carry `route_epoch`, so any sample can be traced to the plan that routed it.
+
+### 4. Trust classes
+
+`resource-offer.trust` is one of five classes, in this order; `workload-requirements.trust` lists the classes allowed
+to run the work:
+
+| Class | Meaning |
+| --- | --- |
+| `first-party` | Hosts and accounts the OpenVibe network operates itself (its own servers and provider accounts). |
+| `user-owned` | A person's own node or machine (an OpenVibe.Node they run), offered for their own workloads. |
+| `partner` | An organisation under a written agreement with the network, bound by its data and uptime terms. |
+| `community` | Capacity volunteered by community members, without a contract; best effort, no private data. |
+| `external` | A third-party provider used through its public API under its own terms (a cloud or AI vendor). |
+
+**`user-owned` is not in the default trust set.** When a requirement names no `trust`, the eligible classes are
+`first-party`, `partner`, `community` and `external` (the SDK's `TRUST_ORDER`). A `user-owned` offer is eligible only
+for a workload whose requirements list `"user-owned"` explicitly. Adding the class to the contracts therefore widens
+no existing caller's eligible set, and the SDK must not add it to its default list.
+
+### 5. Worker capability names a Node advertises
+
+An OpenVibe.Node that runs jobs advertises one `worker:<class>` capability in its `resource-offer.capabilities` for
+each `platform.runtime-class@1` class it runs: `worker:function` (a declared function artifact) and `worker:code`
+(submitted code in a sandbox) today, and `worker:browser`, `worker:linux`, `worker:desktop` and `worker:gpu` as Node
+gains them. These six names are reserved for the Node job worker (`$defs.reservedWorkerCapabilities` in
+`platform.resource-offer@1`); every other `worker:` name (`worker:ffmpeg`, `worker:ai-gpu`) is an ordinary capability.
+A requirement asks for a class by listing the same name in its `capabilities`.
+
+### 6. Control plane and data plane
+
+- The **control plane** collects offers, provider state and rate cards, runs the placer, and issues signed
+ `placement-plan@1` documents, each with a higher `epoch` than the last.
+- The **data plane** routes by the plan it holds. It accepts a new plan only when `openvibe-sdk/placement.verifyPlan`
+ passes (known `key_id`, valid signature, `expires_at` in the future) and the plan's `epoch` is newer than the current
+ one (`createPlanHolder`). A plan that fails is rejected and logged.
+- **The data plane keeps running on the last valid signed plan.** A control-plane outage, a bad signature or a stale
+ epoch never stops delivery; it only stops change. A plan past its `expires_at` is never accepted, and a data plane
+ holding one keeps routing by it, reports itself stale, and does not invent routes of its own.
+
+### 7. The reference consumer
+
+T6 (OpenVibe.AI) is the reference consumer: it states `ai.run` requirements, places each run across first-party
+nodes, external AI providers and, when the request names it, the person's own `user-owned` node, and records a
+placement result, telemetry and usage samples for every run. Other services adopt the fabric after it.
+
+## Consequences
+
+- One vocabulary and one placer for every placed workload; a new kind of capacity is a new offer, not new code.
+- Routing survives the control plane: plans are signed, epoch-ordered and verified where they are used.
+- A person can run their own work on their own machine without that machine joining anyone else's default pool.
+- The SDK placer, the T2 resource registry and OpenVibe.Node must agree on the trust rule; this ADR is that rule.
+
+## Open questions for the owner
+
+1. May a `user-owned` node run **other users'** workloads (as a community-style contributor), or only its owner's?
+ Until answered, only its owner's, and only when the requirement names `user-owned`.
+2. What is the consent and revocation rule for a `user-owned` node: how its owner opts each workload class in, how
+ they withdraw (immediately, or after running jobs drain), and what the placer does with work in flight when consent
+ is revoked?
+
+## What this ADR does not claim
+
+- No producer emits `user-owned` offers yet, and no service places across user-owned nodes today.
+- Cells, the plan issuer and its key distribution are not built; ADR-034's phasing (roadmap WS-W) still applies.
@@ -40,3 +40,4 @@ Platform-wide decisions from the OpenVibe development roadmap (section 10). Each
@@ -40,3 +40,4 @@ Platform-wide decisions from the OpenVibe development roadmap (section 10). Each
| [ADR-034](ADR-034-platform-north-star.md) | Platform north star, an open affordable cloud: boundaries now (projects, `ovrn:` resource names, scoped grants, generated contracts, metering), infrastructure when measured; control/data plane and cells; regions, edge nodes, load balancing and a geo API; pulled, gated deploys; four extension levels under one manifest | Proposed 2026-09-28, for the owner's review |
| [ADR-034](ADR-034-platform-north-star.md) | Platform north star, an open affordable cloud: boundaries now (projects, `ovrn:` resource names, scoped grants, generated contracts, metering), infrastructure when measured; control/data plane and cells; regions, edge nodes, load balancing and a geo API; pulled, gated deploys; four extension levels under one manifest | Proposed 2026-09-28, for the owner's review |
| [ADR-035](ADR-035-postgresql-and-valkey.md) | PostgreSQL 18 as every service's system of record (database + roles per service, PgBouncer transaction pooling, pgBackRest PITR to B2) and Valkey as the shared non-authoritative store; the SDK data layer; services migrate one at a time, smallest first; supersedes ADR-007's trigger rule; amendment: PostgreSQL dialect only (PGlite in tests), a verified SQLite importer, every service async and migrated | Accepted 2026-09-28 (owner decision), amended the same day |
| [ADR-035](ADR-035-postgresql-and-valkey.md) | PostgreSQL 18 as every service's system of record (database + roles per service, PgBouncer transaction pooling, pgBackRest PITR to B2) and Valkey as the shared non-authoritative store; the SDK data layer; services migrate one at a time, smallest first; supersedes ADR-007's trigger rule; amendment: PostgreSQL dialect only (PGlite in tests), a verified SQLite importer, every service async and migrated | Accepted 2026-09-28 (owner decision), amended the same day |
| [ADR-042](ADR-042-events-fabric.md) | The events fabric: the delivery-policy class is the one vocabulary and TOPIC/QUEUE/STREAM a derived carrier class; PostgreSQL stays the record, carriers are transports that fail open to `pg-v1`; a delivery lease before any second worker; per-key ordering; the SDK planner places deliveries and explains them; JetStream for STREAM only; opaque cursors replace the global seq after one overlap release; hot/replay/archive tiers; amends ADR-004's NATS rejection | Accepted 2026-09-29 (plan T7) |
| [ADR-042](ADR-042-events-fabric.md) | The events fabric: the delivery-policy class is the one vocabulary and TOPIC/QUEUE/STREAM a derived carrier class; PostgreSQL stays the record, carriers are transports that fail open to `pg-v1`; a delivery lease before any second worker; per-key ordering; the SDK planner places deliveries and explains them; JetStream for STREAM only; opaque cursors replace the global seq after one overlap release; hot/replay/archive tiers; amends ADR-004's NATS rejection | Accepted 2026-09-29 (plan T7) |
+| [ADR-046](ADR-046-universal-adaptive-fabric.md) | The universal adaptive fabric: offers, requirements, placement results and signed plans through one placer (`openvibe-sdk/placement`); health, cost, telemetry and usage samples; five trust classes, with `user-owned` eligible only when a workload names it; `worker:<class>` Node capabilities; the data plane keeps running on the last valid signed plan; T6 AI the reference consumer | Proposed 2026-10-03 (plan T1 step 3), for the owner's review |
new file mode 100644
new file mode 100644
@@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
+{
+ "offer_id": "off_stranger_1",
+ "kind": "node",
+ "node_id": "stranger-node-1",
+ "provider": "self",
+ "region": "home",
+ "trust": "stranger",
+ "capabilities": [
+ "node:http",
+ "worker:function"
+ ],
+ "health": {
+ "status": "up"
+ },
+ "pricing": {
+ "model": "free-allowance"
+ },
+ "updated_at": "2026-10-03T00:00:00Z"
+}
@@ -7,6 +7,7 @@
@@ -7,6 +7,7 @@
"trust": "user-owned",
"trust": "user-owned",
"capabilities": [
"capabilities": [
"node:http",
"node:http",
+ "worker:function",
"worker:ai-gpu"
"worker:ai-gpu"
],
],
"capacity": {
"capacity": {
@@ -58431,6 +58431,7 @@
@@ -58431,6 +58431,7 @@
},
},
"trust": {
"trust": {
"type": "string",
"type": "string",
+ "description": "the offer's trust class (ADR-046): first-party (operated by the OpenVibe network), user-owned (a person's own node, eligible only for workloads whose requirements name user-owned), partner, community or external",
"enum": [
"enum": [
"first-party",
"first-party",
"user-owned",
"user-owned",
@@ -58888,7 +58889,7 @@
@@ -58888,7 +58889,7 @@
]
]
},
},
"minItems": 1,
"minItems": 1,
- "description": "trust levels allowed to run it"
+ "description": "trust classes allowed to run it (ADR-046); when absent, first-party, partner, community and external. user-owned is never a default: a user-owned offer is eligible only when this list names it"
},
},
"region": {
"region": {
"type": "string",
"type": "string",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.AI API",
"title": "OpenVibe.AI API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Billing API",
"title": "OpenVibe.Billing API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Blog API",
"title": "OpenVibe.Blog API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Bot API",
"title": "OpenVibe.Bot API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Chat API",
"title": "OpenVibe.Chat API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Codes API",
"title": "OpenVibe.Codes API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Community API",
"title": "OpenVibe.Community API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Coupons API",
"title": "OpenVibe.Coupons API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Deals API",
"title": "OpenVibe.Deals API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Events API",
"title": "OpenVibe.Events API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Games API",
"title": "OpenVibe.Games API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Host API",
"title": "OpenVibe.Host API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Live API",
"title": "OpenVibe.Live API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Media API",
"title": "OpenVibe.Media API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Network API",
"title": "OpenVibe.Network API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.News API",
"title": "OpenVibe.News API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenRe.Stream API",
"title": "OpenRe.Stream API",
- "version": "0.87.0",
- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Reviews API",
"title": "OpenVibe.Reviews API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Search API",
"title": "OpenVibe.Search API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Sources API",
"title": "OpenVibe.Sources API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Space API",
"title": "OpenVibe.Space API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tips API",
"title": "OpenVibe.Tips API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tools API",
"title": "OpenVibe.Tools API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Trade API",
"title": "OpenVibe.Trade API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.VIP API",
"title": "OpenVibe.VIP API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Wiki API",
"title": "OpenVibe.Wiki API",
- "version": "0.87.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.87.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.88.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.88.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -50970,6 +50970,9 @@ export type ResourceOffer = {
@@ -50970,6 +50970,9 @@ export type ResourceOffer = {
adapter?: string;
adapter?: string;
region: string;
region: string;
cell?: string;
cell?: string;
+ /**
+ * the offer's trust class (ADR-046): first-party (operated by the OpenVibe network), user-owned (a person's own node, eligible only for workloads whose requirements name user-owned), partner, community or external
+ */
trust: "first-party" | "user-owned" | "partner" | "community" | "external";
trust: "first-party" | "user-owned" | "partner" | "community" | "external";
/**
/**
* e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2. Kind harness: task:edit, task:review, task:browse, task:run, task:test and task:plan for the task kinds it takes (its detail's task_capabilities), and harness:mcp, harness:resume, harness:host-access and harness:long-autonomy for its detail's capabilities that are true. The `worker:<class>` names that map to platform.runtime-class@1 values are reserved for OpenVibe.Node's job worker, one per class: worker:function, worker:code, worker:browser, worker:linux, worker:desktop and worker:gpu ($defs.reservedWorkerCapabilities). A node advertises the name of each class it runs (Node's status.capabilities.worker); every other `worker:` name (worker:ffmpeg, worker:ai-gpu) is an ordinary capability name.
* e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2. Kind harness: task:edit, task:review, task:browse, task:run, task:test and task:plan for the task kinds it takes (its detail's task_capabilities), and harness:mcp, harness:resume, harness:host-access and harness:long-autonomy for its detail's capabilities that are true. The `worker:<class>` names that map to platform.runtime-class@1 values are reserved for OpenVibe.Node's job worker, one per class: worker:function, worker:code, worker:browser, worker:linux, worker:desktop and worker:gpu ($defs.reservedWorkerCapabilities). A node advertises the name of each class it runs (Node's status.capabilities.worker); every other `worker:` name (worker:ffmpeg, worker:ai-gpu) is an ordinary capability name.
@@ -51074,7 +51077,7 @@ export interface WorkloadRequirements {
@@ -51074,7 +51077,7 @@ export interface WorkloadRequirements {
*/
*/
residency?: string;
residency?: string;
/**
/**
- * trust levels allowed to run it
+ * trust classes allowed to run it (ADR-046); when absent, first-party, partner, community and external. user-owned is never a default: a user-owned offer is eligible only when this list names it
*
*
* @minItems 1
* @minItems 1
*/
*/
new file mode 100644
new file mode 100644
@@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
+{
+ "domain": "openvibe.work",
+ "tld": "work",
+ "name": "OpenVibe.Work",
+ "icon": "fa-briefcase",
+ "accent": "#34d399",
+ "tagline": "Find work, with an agent on your side.",
+ "description": "OpenVibe.Work will gather job listings from open sources with their provenance, let you search them and watch for new ones that fit, and let an agent help you prepare and apply — with your approval at every step. Part of the OpenVibe network, with one account for every site.",
+ "pillars": [
+ {
+ "title": "Every listing sourced",
+ "text": "Where it came from and when, always shown."
+ },
+ {
+ "title": "Watch for fits",
+ "text": "Get told when a job that matches appears."
+ },
+ {
+ "title": "An agent that helps",
+ "text": "Drafts, tailors and tracks applications — you approve."
+ },
+ {
+ "title": "Yours to control",
+ "text": "Your profile and history stay yours, exportable any time."
+ }
+ ],
+ "keywords": "openvibe work, job search, job listings, job alerts, ai job application assistant",
+ "launch": "job listings and alerts",
+ "relationships": {
+ "noRepo": true,
+ "meanwhile": [
+ {
+ "label": "OpenVibe.Network",
+ "url": "https://openvibe.network",
+ "note": "One free account for every OpenVibe site, ready today."
+ }
+ ]
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
+{
+ "domain": "openvibe.zone",
+ "tld": "zone",
+ "name": "OpenVibe.Zone",
+ "icon": "fa-box-archive",
+ "accent": "#2dd4bf",
+ "tagline": "Your data, in zones.",
+ "description": "OpenVibe.Zone will keep your data and files in zones — S3-style buckets for persistent storage that every OpenVibe service and agent can use with your permission, also offered to developers through OpenVibe.Services. Part of the OpenVibe network, with one account for every site.",
+ "pillars": [
+ {
+ "title": "An S3-style API",
+ "text": "Buckets, objects and signed links that existing tools already speak."
+ },
+ {
+ "title": "Durable storage",
+ "text": "Files and data that stay put, with copies kept safe."
+ },
+ {
+ "title": "Access you control",
+ "text": "Decide which services, agents and people can read or write each zone."
+ },
+ {
+ "title": "Used across the network",
+ "text": "The same zones back your streams, media, agents and apps."
+ }
+ ],
+ "keywords": "openvibe zone, object storage, s3 compatible storage, buckets, persistent storage, file storage api",
+ "launch": "zones for data and files",
+ "relationships": {
+ "service": "zone",
+ "plannedRepo": "OpenVibe.Zone",
+ "meanwhile": [
+ {
+ "label": "OpenVibe.Network",
+ "url": "https://openvibe.network",
+ "note": "One free account for every OpenVibe site, ready today."
+ }
+ ]
+ }
+}
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.87.0",
+ "version": "0.88.0",
"lockfileVersion": 3,
"lockfileVersion": 3,
"requires": true,
"requires": true,
"packages": {
"packages": {
"": {
"": {
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.87.0",
+ "version": "0.88.0",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.87.0",
+ "version": "0.88.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"main": "index.js",
"types": "generated/typescript/index.d.ts",
"types": "generated/typescript/index.d.ts",
@@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
{
{
- "_about": "The 33 domains of OpenVibe.Sites sites.json (origin/main c2fda6c, 2026-09-29), snapshotted for the catalog home test (plan T11 lane D). Each has exactly one home: a service manifest site with a tld, or manifests/products/<domain>.json.",
+ "_about": "The 35 catalog domains of OpenVibe.Sites sites.json: the 33 of origin/main c2fda6c (2026-09-29), snapshotted for the catalog home test (plan T11 lane D), plus openvibe.work and openvibe.zone (plan T11 step 1). Each has exactly one home: a service manifest site with a tld, or manifests/products/<domain>.json.",
"source": "OpenVibers/OpenVibe.Sites@c2fda6c:sites.json",
"source": "OpenVibers/OpenVibe.Sites@c2fda6c:sites.json",
"domains": [
"domains": [
"openvibe.news",
"openvibe.news",
@@ -34,6 +34,8 @@
@@ -34,6 +34,8 @@
"openvibe.bot",
"openvibe.bot",
"openvibe.watch",
"openvibe.watch",
"openvibe.actor",
"openvibe.actor",
- "openvibe.events"
+ "openvibe.events",
+ "openvibe.work",
+ "openvibe.zone"
]
]
}
}
@@ -84,4 +84,16 @@ checks++;
@@ -84,4 +84,16 @@ checks++;
assert.strictEqual(contracts.validate('platform.workload-requirements', { ...userRequirement, trust: ['other'] }).valid, false, 'an unknown requirement trust is refused');
assert.strictEqual(contracts.validate('platform.workload-requirements', { ...userRequirement, trust: ['other'] }).valid, false, 'an unknown requirement trust is refused');
checks++;
checks++;
+// The fixtures (ADR-046): a user-owned node advertises the Node job worker's worker:function, and the
+// unknown-trust offer is refused for its trust alone.
+const fixture = (p) => require(`../fixtures/platform.resource-offer/${p}.json`);
+const userNode = fixture('valid/user-owned-node');
+assert.ok(userNode.trust === 'user-owned' && userNode.capabilities.includes('worker:function'), 'user-owned-node advertises worker:function');
+checks++;
+const unknownTrust = contracts.validate('platform.resource-offer', fixture('invalid/unknown-trust'));
+assert.strictEqual(unknownTrust.valid, false, 'the unknown-trust fixture is refused');
+checks++;
+assert.ok(unknownTrust.errors.length && unknownTrust.errors.every(e => e.path === '/trust'), `unknown-trust fails on /trust only: ${JSON.stringify(unknownTrust.errors)}`);
+checks++;
+
console.log(`platform fabric: ${checks} assertions passed`);
console.log(`platform fabric: ${checks} assertions passed`);
@@ -29,7 +29,7 @@ for (const row of products.catalog()) homes.set(row.domain, [...(homes.get(row.d
@@ -29,7 +29,7 @@ for (const row of products.catalog()) homes.set(row.domain, [...(homes.get(row.d
for (const [domain, list] of homes) ok(list.length === 1, `${domain} has one home, not ${list.join(', ')}`);
for (const [domain, list] of homes) ok(list.length === 1, `${domain} has one home, not ${list.join(', ')}`);
const snapshot = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures/sites-domains.json'), 'utf8')).domains;
const snapshot = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures/sites-domains.json'), 'utf8')).domains;
-ok(snapshot.length === 33 && new Set(snapshot).size === 33, 'the Sites snapshot holds 33 distinct domains');
+ok(snapshot.length === 35 && new Set(snapshot).size === 35, 'the Sites snapshot holds 35 distinct domains');
for (const d of snapshot) ok((homes.get(d) || []).length === 1, `${d} (OpenVibe.Sites) has exactly one Contracts home: ${(homes.get(d) || ['none']).join(', ')}`);
for (const d of snapshot) ok((homes.get(d) || []).length === 1, `${d} (OpenVibe.Sites) has exactly one Contracts home: ${(homes.get(d) || ['none']).join(', ')}`);
for (const p of products.manifests) {
for (const p of products.manifests) {