openvibers/openvibe.contracts Commits
0.372.0: OpenVibe Link renames the machine contracts, principals and AI provider (#386)
Changes
@@@ -1,5 +1,17 @@# Changelog++## 0.372.0 — 2026-11-09++++- The machine product Node became OpenVibe Link. The network registry contracts are `network.link@1`,++ `network.link-report-request@1` and `network.link-list-result@1`, the event is `network.link.revoked`, and the platform++ carries `platform.link-capabilities@1` and `link.compute@1`. The capabilities `network.node.manage`,++ `network.node.report` and `network.node.self.manage` are `network.link.manage`, `network.link.report` and++ `network.link.self.manage`; their internal routes and the manifests follow.++- A paired machine is a `link` principal: identity.service-token-claims actor_type `node` is `link` with sub `link:lnk_…`,++ and `ids.principalSub` mints `link:lnk_…` (the old prefix and ids are gone, no alias). The AI provider `node` is `link`++ in ai.credential-put@1/@2 and ai.credential@1/@2. The old contract ids are deprecated with their Link replacements; the++ schema corrections are recorded against the current release and the ones before it.++## 0.371.0 — 2026-11-09- Exchange API platform and search: exchange.key@1, exchange.key-created@1, exchange.usage@1, exchange.deployments@1, exchange.search-result@1, exchange.image-result@1, and the Exchange manifest's routes@@@ -851,7 +863,7 @@## 0.261.0 — 2026-10-31- **The machines hub** (plan L34, steps M2 and M4). A person's computers are the network's: Bot stays the device-- registry underneath, and Network fronts it on openvibe.network/machines for the person and for apps the person++ registry underneath, and Network fronts it on openvibe.link/machines for the person and for apps the personconsented to.- **`network.machine@1`:** one machine as the hub shows it — its id (Bot's `dev_…`), name, kind, os, whether it isonline, when it was last seen, its `roles` and what it does right now (`running`: its Codes jobs, its desks, its@@@ -3119,7 +3131,7 @@ contract changes.**The node-revoked event contract** (plan T2 follow-up; the deferred item in OpenVibe.Network's`docs/t2-cells-and-node-principal.md` section 10, "Revocation latency outside Bot"). Adds--`network.node.revoked@1` (owner `network`, first-party, active): a node principal (`nod_…`), a person's paired++`network.link.revoked@1` (owner `network`, first-party, active): a node principal (`lnk_…`), a person's pairedmachine, was revoked through `POST /internal/node-principals/:id/revoke` (`network.node.manage`, the service thatpaired it) or `POST /api/v1/me/nodes/:id/revoke` (the owner's session), or with the owner's account. The payload is`{ node_id, principal_id, owner, reason?, at }` (`extra` fields refused): `owner` is the person, project or platform@@@ -3523,9 +3535,9 @@ Network grants for the usage producers follow this release. Additive.**Node principals, node capabilities and per-kind resource offers** (plan T1 lane A, the contracts brief ofOpenVibe.Network `docs/t2-cells-and-node-principal.md` §9.1):--- `lib/ids.js`: `PREFIX.node = 'nod'`; `principalSub({ type: 'node', id })` gives `node:nod_<ULID>`. A node is a++- `lib/ids.js`: `PREFIX.node = 'nod'`; `principalSub({ type: 'link', id })` gives `link:lnk_<ULID>`. A node is aprincipal only: `node` is not a `SUBJECT_TYPES` entry nor an `identity.subject-ref@1` type.--- `identity.service-token-claims`: `sub` also accepts `node:nod_<ULID>` and `actor_type` gains `node`, so++- `identity.service-token-claims`: `sub` also accepts `link:lnk_<ULID>` and `actor_type` gains `link`, so`verifyServiceToken` accepts a node token and still rejects a malformed node id. Fixtures `node-token` (valid) and`node-bad-id` (invalid).- New capabilities `network.node.manage` (a service mints pairing codes and reads or revokes the node principals it@@@ -242,7 +242,7 @@ A job moves `queued → placed → running` and ends `succeeded`, `failed`, `can`run.job.queued|started|succeeded|failed|cancelled|expired`. Run mints the job id (`job_<ULID>`, `ids.newId('job')`)and passes it unchanged as the `platform.job@1` id, so every metering reading is `run:<job id>:<n>` (service `run`,operation `function.invoke`, unit `s`) and `usage.seconds` is their sum. Inputs are Media objects pinned by sha256--(never a URL); `egress` takes Node's `worker.egress` values (`none` by default, `public`, `openvibe-only`).++(never a URL); `egress` takes Link's `worker.egress` values (`none` by default, `public`, `openvibe-only`).## Versioning and compatibility@@@ -2,14 +2,14 @@"_about": "Active capabilities still without an input or output schema (roadmap WS-C task 4). test/capability-schemas.test.js fails on any active capability missing a schema that is not listed here, and on a listed one that has since gained it (remove it from this list). The list only shrinks: fill a capability's schemas, then delete its row. A request with no body names common.no-body@1; a response of bytes names common.binary@1.","gaps": [{-- "id": "network.node.manage",++ "id": "network.link.manage","missing": ["inputSchema","outputSchema"]},{-- "id": "network.node.self.manage",++ "id": "network.link.self.manage","missing": ["inputSchema","outputSchema"@@@ -18,6 +18,78 @@"base": "v0.289.0","fixedIn": "0.289.1","reason": "0.289.0 made `project_id` optional on billing.credit-hold-create-request@1 for a personal hold, but billing.credit-hold@1 still required a string, so the answer Billing's presentHold sends for a personal hold (`project_id: null`) did not validate. The string-only rule never described a personal hold's answer; project holds still send a string, and `project_id` widens to optional [string, null]."++ },++ {++ "id": "ai.credential-put",++ "base": "v0.352.0",++ "fixedIn": "0.372.0",++ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential-put@1 and @2 is the same provider under its new name. Only our own AI service ever produced it, and it moves to `link` in the same release, so the enum is corrected in place rather than carried as an alias."++ },++ {++ "id": "ai.credential",++ "base": "v0.352.0",++ "fixedIn": "0.372.0",++ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential@1 and @2 is the same provider under its new name, produced and read only by our own AI service, which moves to `link` in the same release."++ },++ {++ "id": "identity.service-token-claims",++ "base": "v0.352.0",++ "fixedIn": "0.372.0",++ "reason": "The machine product Node became OpenVibe Link: a paired machine's principal actor_type is `link` (sub `link:lnk_…`) instead of `node`. Network mints these tokens and every receiver is ours, updated in the same release; no third party consumed the old value."++ },++ {++ "id": "ai.credential-put",++ "base": "v0.365.0",++ "fixedIn": "0.372.0",++ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential-put@1 and @2 is the same provider under its new name. Only our own AI service ever produced it, and it moves to `link` in the same release, so the enum is corrected in place rather than carried as an alias."++ },++ {++ "id": "ai.credential",++ "base": "v0.365.0",++ "fixedIn": "0.372.0",++ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential@1 and @2 is the same provider under its new name, produced and read only by our own AI service, which moves to `link` in the same release."++ },++ {++ "id": "identity.service-token-claims",++ "base": "v0.365.0",++ "fixedIn": "0.372.0",++ "reason": "The machine product Node became OpenVibe Link: a paired machine's principal actor_type is `link` (sub `link:lnk_…`) instead of `node`. Network mints these tokens and every receiver is ours, updated in the same release; no third party consumed the old value."++ },++ {++ "id": "ai.credential-put",++ "base": "v0.368.0",++ "fixedIn": "0.372.0",++ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential-put@1 and @2 is the same provider under its new name. Only our own AI service ever produced it, and it moves to `link` in the same release, so the enum is corrected in place rather than carried as an alias."++ },++ {++ "id": "ai.credential",++ "base": "v0.368.0",++ "fixedIn": "0.372.0",++ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential@1 and @2 is the same provider under its new name, produced and read only by our own AI service, which moves to `link` in the same release."++ },++ {++ "id": "identity.service-token-claims",++ "base": "v0.368.0",++ "fixedIn": "0.372.0",++ "reason": "The machine product Node became OpenVibe Link: a paired machine's principal actor_type is `link` (sub `link:lnk_…`) instead of `node`. Network mints these tokens and every receiver is ours, updated in the same release; no third party consumed the old value."++ },++ {++ "id": "ai.credential-put",++ "base": "v0.371.0",++ "fixedIn": "0.372.0",++ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential-put@1 and @2 is the same provider under its new name. Only our own AI service ever produced it, and it moves to `link` in the same release, so the enum is corrected in place rather than carried as an alias."++ },++ {++ "id": "ai.credential",++ "base": "v0.371.0",++ "fixedIn": "0.372.0",++ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential@1 and @2 is the same provider under its new name, produced and read only by our own AI service, which moves to `link` in the same release."++ },++ {++ "id": "identity.service-token-claims",++ "base": "v0.371.0",++ "fixedIn": "0.372.0",++ "reason": "The machine product Node became OpenVibe Link: a paired machine's principal actor_type is `link` (sub `link:lnk_…`) instead of `node`. Network mints these tokens and every receiver is ours, updated in the same release; no third party consumed the old value."}]}@@@ -231,6 +231,41 @@"since": "0.263.0","removeAfter": "1.0.0","reason": "Report's personal AI usage answers rows, totals, by_paid_by, insights and source (plan L41 R1); v1's usage array and its field names never matched the route, so the shape moved to a new major (2026-10-31)"++ },++ {++ "id": "network.node",++ "replacement": "network.link@1",++ "since": "0.372.0",++ "removeAfter": "1.0.0",++ "reason": "The machine product Node became OpenVibe Link: network.node@1 became network.link@1 in the rename (2026-11-09)"++ },++ {++ "id": "network.node-report-request",++ "replacement": "network.link-report-request@1",++ "since": "0.372.0",++ "removeAfter": "1.0.0",++ "reason": "The machine product Node became OpenVibe Link: network.node-report-request@1 became network.link-report-request@1 in the rename (2026-11-09)"++ },++ {++ "id": "network.node-list-result",++ "replacement": "network.link-list-result@1",++ "since": "0.372.0",++ "removeAfter": "1.0.0",++ "reason": "The machine product Node became OpenVibe Link: network.node-list-result@1 became network.link-list-result@1 in the rename (2026-11-09)"++ },++ {++ "id": "platform.node-capabilities",++ "replacement": "platform.link-capabilities@1",++ "since": "0.372.0",++ "removeAfter": "1.0.0",++ "reason": "The machine product Node became OpenVibe Link: platform.node-capabilities@1 became platform.link-capabilities@1, and link.compute@1 carries the Link's offer in the platform fabric (2026-11-09)"++ },++ {++ "id": "network.node.revoked",++ "replacement": "network.link.revoked@1",++ "since": "0.372.0",++ "removeAfter": "1.0.0",++ "reason": "The machine product Node became OpenVibe Link: the network.node.revoked event became network.link.revoked and its principal ids move to lnk_ (2026-11-09)"}]}@@@ -13,14 +13,14 @@"enum": ["openai","anthropic",-- "node"++ "link"],"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."},"machine": {"type": "string","pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",-- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."++ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."},"base_url": {"type": "string",@@@ -78,7 +78,7 @@"if": {"properties": {"provider": {-- "const": "node"++ "const": "link"}},"required": [@@@ -17,7 +17,7 @@"minimax","openrouter","custom",-- "node"++ "link"],"description": "anthropic = Anthropic's Messages API; openai = any OpenAI-compatible endpoint; deepseek, minimax and openrouter = those providers; custom = the endpoint in base_url (a URL that speaks the OpenAI or the Anthropic format, by the route it is used on); node = the person's own machine (no key, no base_url, no cost)."},@@@ -42,7 +42,7 @@"machine": {"type": "string","pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",-- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model, or `any` of their own."++ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model, or `any` of their own."},"models": {"type": "object",@@@ -89,7 +89,7 @@"if": {"properties": {"provider": {-- "const": "node"++ "const": "link"}},"required": [@@@ -26,7 +26,7 @@"enum": ["openai","anthropic",-- "node"++ "link"]},"machine": {@@@ -35,7 +35,7 @@"null"],"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",-- "description": "provider node: the machine it runs on, or `any` of the person's own (contracts 0.221.0)"++ "description": "provider link: the machine it runs on, or `any` of the person's own (contracts 0.221.0)"},"base_url": {"type": [@@@ -30,7 +30,7 @@"minimax","openrouter","custom",-- "node"++ "link"]},"machine": {@@@ -39,7 +39,7 @@"null"],"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",-- "description": "provider node: the machine it runs on, or `any` of the person's own"++ "description": "provider link: the machine it runs on, or `any` of the person's own"},"base_url": {"type": [@@@ -118,7 +118,7 @@"properties": {"provider": {"not": {-- "const": "node"++ "const": "link"}}},@@@ -39,7 +39,7 @@},"operator": {"type": "string",-- "description": "Operator slug or `owner:<subject_id>` for a private node."++ "description": "Operator slug or `owner:<subject_id>` for a private machine."},"host": {"type": [@@@ -158,7 +158,7 @@"node","submitted"],-- "description": "codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it."++ "description": "codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it."},"id": {"type": "string",@@@ -158,7 +158,7 @@"node","submitted"],-- "description": "codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it."++ "description": "codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it."},"id": {"type": "string",@@@ -79,7 +79,7 @@"null"],"maxLength": 128,-- "description": "The offer's provider, as Run sent it; null for a first-party node."++ "description": "The offer's provider, as Run sent it; null for a first-party machine."},"cancel_requested": {"type": "boolean",@@@ -2,7 +2,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/bot/machine-pool.v1.json","title": "BotMachinePool",-- "description": "bot.machine-pool@1 (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC, `rpc-server` on the members and `llama-server --rpc` on the head. OpenVibe.Network holds the record: the owner proposes it on /machines (POST /api/v1/machine-pools, two to eight of their own machines and an optional head; the default head has the most free VRAM), and each machine reads it with its own node token (GET /api/v1/node/self/pools/:id, network.node.self.manage) and approves it locally (POST /api/v1/node/self/pools/:id/approve with { certificate }). A machine's `certificate` is null until it approves on that machine: the Link generates an Ed25519 key there, keeps the private key, and uploads only its self-signed public certificate, which the other members pin for the pool's private link. A pool with any null certificate never serves. The record never carries a private key or a prompt. What a schema cannot say, Network and Node check at run time: the members are distinct and the head is one of them; every member has the pool's owner, today, through Bot; every member was seen from the same public address with a private address in the same subnet; the head's direct link probe to each member passes; each machine's certificate is its own; and any change of membership, owner, head or key revokes the pool, so all machines approve a new one. A machine that leaves (removed, signed out, a different owner) drops its link and its key at once.",++ "description": "bot.machine-pool@1 (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC, `rpc-server` on the members and `llama-server --rpc` on the head. OpenVibe.Network holds the record: the owner proposes it on /machines (POST /api/v1/machine-pools, two to eight of their own machines and an optional head; the default head has the most free VRAM), and each machine reads it with its own Link token (GET /api/v1/link/self/pools/:id, network.link.self.manage) and approves it locally (POST /api/v1/link/self/pools/:id/approve with { certificate }). A machine's `certificate` is null until it approves on that machine: the Link generates an Ed25519 key there, keeps the private key, and uploads only its self-signed public certificate, which the other members pin for the pool's private link. A pool with any null certificate never serves. The record never carries a private key or a prompt. What a schema cannot say, Network and Link check at run time: the members are distinct and the head is one of them; every member has the pool's owner, today, through Bot; every member was seen from the same public address with a private address in the same subnet; the head's direct link probe to each member passes; each machine's certificate is its own; and any change of membership, owner, head or key revokes the pool, so all machines approve a new one. A machine that leaves (removed, signed out, a different owner) drops its link and its key at once.","type": "object","additionalProperties": false,"required": [@@@ -6436,30 +6436,30 @@"status": "retired"},{-- "id": "network.node",++ "id": "network.link","version": "1.0.0","owner": "network",-- "schema": "network/node.v1.json",++ "schema": "network/link.v1.json","visibility": "public","compatibility": "backward","status": "active","adr": "ADR-034"},{-- "id": "network.node-report-request",++ "id": "network.link-report-request","version": "1.0.0","owner": "network",-- "schema": "network/node-report-request.v1.json",++ "schema": "network/link-report-request.v1.json","visibility": "first-party","compatibility": "backward","status": "active","adr": "ADR-034"},{-- "id": "network.node-list-result",++ "id": "network.link-list-result","version": "1.0.0","owner": "network",-- "schema": "network/node-list-result.v1.json",++ "schema": "network/link-list-result.v1.json","visibility": "public","compatibility": "backward","status": "active",@@@ -6728,10 +6728,19 @@"adr": "ADR-034"},{-- "id": "platform.node-capabilities",++ "id": "link.compute","version": "1.0.0","owner": "network",-- "schema": "platform/node-capabilities.v1.json",++ "schema": "link/compute.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "platform.link-capabilities",++ "version": "1.0.0",++ "owner": "network",++ "schema": "platform/link-capabilities.v1.json","visibility": "public","compatibility": "backward","status": "active",@@@ -7391,10 +7400,10 @@"adr": "ADR-048"},{-- "id": "network.node.revoked",++ "id": "network.link.revoked","version": "1.0.0","owner": "network",-- "schema": "events/payloads/network.node.revoked.v1.json",++ "schema": "events/payloads/network.link.revoked.v1.json","visibility": "first-party","compatibility": "backward","status": "active",@@@ -83,7 +83,7 @@"string","null"],-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$","description": "The Network node principal the device presents; null for an agent or server that joins through a pre-auth key."},"name": {nnew file mode 100644@@@ -0,0 +1,68 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/network.link.revoked.v1.json",++ "title": "NetworkLinkRevokedPayload",++ "description": "network.link.revoked v1 (OpenVibe.Network; plan T2, docs/t2-cells-and-node-principal.md section 10, migrations/0014_node_pairing.sql). A node principal (lnk_…), a person's paired machine, was revoked: through POST /internal/link-principals/:id/revoke (network.link.manage, the service that paired it), through POST /api/v1/me/nodes/:id/revoke (the owner's session), or with the owner's account. Network revokes the row and clears the credential and its grace window; a machine holding a live node token is refused at its next use (≤ 300 s), and until this event only learned of the revoke at its next reauth (≤ 330 s). A consumer stops the machine at once; a second revoke changes nothing and emits nothing. The credential and its hash are never carried. Envelope: subject { type: node_principal, id: <lnk_…> }, visibility internal, actor the service (svc:<service>) or the owner ({ type: user }) that revoked.",++ "type": "object",++ "required": [++ "node_id",++ "principal_id",++ "owner",++ "at"++ ],++ "additionalProperties": false,++ "properties": {++ "node_id": {++ "type": "string",++ "pattern": "^n-[a-z0-9]{26}$",++ "description": "The machine's node id (platform_node_principals.node_id), minted with the principal and unique among principals."++ },++ "principal_id": {++ "type": "string",++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The revoked node principal. Also the envelope subject id."++ },++ "owner": {++ "description": "Who the machine belongs to, as GET /api/v1/me/nodes and the operator list show it: the person who paired it, the project it is paired into, or the platform for a Host-reported machine.",++ "oneOf": [++ {++ "type": "object",++ "required": ["kind", "subject"],++ "additionalProperties": false,++ "properties": {++ "kind": { "const": "user" },++ "subject": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" }++ }++ },++ {++ "type": "object",++ "required": ["kind", "project_id"],++ "additionalProperties": false,++ "properties": {++ "kind": { "const": "project" },++ "project_id": { "type": "string", "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$" }++ }++ },++ {++ "type": "object",++ "required": ["kind"],++ "additionalProperties": false,++ "properties": {++ "kind": { "const": "platform" }++ }++ }++ ]++ },++ "reason": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Why it was revoked, when the caller gave one (the owner's text or the service's); absent otherwise."++ },++ "at": {++ "type": "string",++ "format": "date-time",++ "description": "When the revoke was applied (platform_node_principals.revoked_at)."++ }++ }++}@@@ -8,7 +8,7 @@"additionalProperties": false,"properties": {"machine_id": { "type": "string", "pattern": "^dev_[0-9A-HJKMNP-TV-Z]{26}$" },-- "principal_id": { "type": "string", "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's node principal on Network, when it has one." },++ "principal_id": { "type": "string", "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's node principal on Network, when it has one." },"owner": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The person the machine belongs to." },"at": { "type": "string", "format": "date-time" }}@@@ -8,7 +8,7 @@"additionalProperties": false,"properties": {"machine_id": { "type": "string", "pattern": "^dev_[0-9A-HJKMNP-TV-Z]{26}$" },-- "principal_id": { "type": "string", "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's node principal on Network, when it has one." },++ "principal_id": { "type": "string", "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's node principal on Network, when it has one." },"owner": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The person the machine belongs to." },"at": { "type": "string", "format": "date-time" }}@@@ -2,14 +2,14 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/events/payloads/network.machine.paired.v1.json","title": "NetworkMachinePairedPayload",-- "description": "network.machine.paired v1 (OpenVibe.Network, plan L34: the machines hub). A machine was paired with a person's account: it redeemed the one-time code and became theirs. It holds a credential from then on; the code is spent. The machine's device id in Bot is absent until the machine checks in for the first time. Envelope: subject { type: machine, id: <nod_…> }, visibility internal, actor service:network.",++ "description": "network.machine.paired v1 (OpenVibe.Network, plan L34: the machines hub). A machine was paired with a person's account: it redeemed the one-time code and became theirs. It holds a credential from then on; the code is spent. The machine's device id in Bot is absent until the machine checks in for the first time. Envelope: subject { type: machine, id: <lnk_…> }, visibility internal, actor service:network.","type": "object","required": ["principal_id", "owner", "at"],"additionalProperties": false,"properties": {"machine_id": { "type": "string", "pattern": "^dev_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's device id in Bot's registry, when Bot already knows the device." },"node_id": { "type": "string", "pattern": "^n-[a-z0-9]{26}$", "description": "The machine's node id (platform_node_principals.node_id)." },-- "principal_id": { "type": "string", "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The node principal the pairing created. Also the envelope subject id." },++ "principal_id": { "type": "string", "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The node principal the pairing created. Also the envelope subject id." },"owner": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The person the machine belongs to." },"at": { "type": "string", "format": "date-time" }}@@@ -8,7 +8,7 @@"additionalProperties": false,"properties": {"machine_id": { "type": "string", "pattern": "^dev_[0-9A-HJKMNP-TV-Z]{26}$" },-- "principal_id": { "type": "string", "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's node principal on Network, when it had one." },++ "principal_id": { "type": "string", "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's node principal on Network, when it had one." },"owner": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The person the machine belonged to." },"reason": { "type": "string", "minLength": 1, "maxLength": 500, "description": "Why it was revoked, when the caller gave one. Absent otherwise." },"at": { "type": "string", "format": "date-time" }ddeleted file mode 100644@@@ -1,68 +0,0 @@--{-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/network.node.revoked.v1.json",-- "title": "NetworkNodeRevokedPayload",-- "description": "network.node.revoked v1 (OpenVibe.Network; plan T2, docs/t2-cells-and-node-principal.md section 10, migrations/0014_node_pairing.sql). A node principal (nod_…), a person's paired machine, was revoked: through POST /internal/node-principals/:id/revoke (network.node.manage, the service that paired it), through POST /api/v1/me/nodes/:id/revoke (the owner's session), or with the owner's account. Network revokes the row and clears the credential and its grace window; a machine holding a live node token is refused at its next use (≤ 300 s), and until this event only learned of the revoke at its next reauth (≤ 330 s). A consumer stops the machine at once; a second revoke changes nothing and emits nothing. The credential and its hash are never carried. Envelope: subject { type: node_principal, id: <nod_…> }, visibility internal, actor the service (svc:<service>) or the owner ({ type: user }) that revoked.",-- "type": "object",-- "required": [-- "node_id",-- "principal_id",-- "owner",-- "at"-- ],-- "additionalProperties": false,-- "properties": {-- "node_id": {-- "type": "string",-- "pattern": "^n-[a-z0-9]{26}$",-- "description": "The machine's node id (platform_node_principals.node_id), minted with the principal and unique among principals."-- },-- "principal_id": {-- "type": "string",-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "The revoked node principal. Also the envelope subject id."-- },-- "owner": {-- "description": "Who the machine belongs to, as GET /api/v1/me/nodes and the operator list show it: the person who paired it, the project it is paired into, or the platform for a Host-reported machine.",-- "oneOf": [-- {-- "type": "object",-- "required": ["kind", "subject"],-- "additionalProperties": false,-- "properties": {-- "kind": { "const": "user" },-- "subject": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" }-- }-- },-- {-- "type": "object",-- "required": ["kind", "project_id"],-- "additionalProperties": false,-- "properties": {-- "kind": { "const": "project" },-- "project_id": { "type": "string", "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$" }-- }-- },-- {-- "type": "object",-- "required": ["kind"],-- "additionalProperties": false,-- "properties": {-- "kind": { "const": "platform" }-- }-- }-- ]-- },-- "reason": {-- "type": "string",-- "minLength": 1,-- "maxLength": 500,-- "description": "Why it was revoked, when the caller gave one (the owner's text or the service's); absent otherwise."-- },-- "at": {-- "type": "string",-- "format": "date-time",-- "description": "When the revoke was applied (platform_node_principals.revoked_at)."-- }-- }--}@@@ -23,14 +23,14 @@},"sub": {"type": "string",-- "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|node:nod_[0-9A-HJKMNP-TV-Z]{26}|agent:agt_[0-9A-HJKMNP-TV-Z]{26})$"++ "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|link:lnk_[0-9A-HJKMNP-TV-Z]{26}|agent:agt_[0-9A-HJKMNP-TV-Z]{26})$"},"actor_type": {"enum": ["service","app","mod",-- "node",++ "link","agent"]},nnew file mode 100644@@@ -0,0 +1,69 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/link/compute.v1.json",++ "title": "LinkCompute",++ "description": "Where a person's work can run. Only ready candidates may be picked; a missing rental quote is never a price.",++ "type": "object",++ "additionalProperties": false,++ "required": ["sources", "candidates"],++ "properties": {++ "sources": {++ "type": "array",++ "items": { "$ref": "#/$defs/source" }++ },++ "candidates": {++ "type": "array",++ "items": { "$ref": "#/$defs/candidate" }++ }++ },++ "$defs": {++ "kind": { "enum": ["personal", "cloud", "exchange", "community"] },++ "status": { "enum": ["ready", "unavailable", "coming_soon"] },++ "price": {++ "type": ["object", "null"],++ "additionalProperties": false,++ "required": ["amount_usd", "unit"],++ "properties": {++ "amount_usd": { "type": "number", "minimum": 0 },++ "unit": { "enum": ["hour"] }++ }++ },++ "source": {++ "type": "object",++ "additionalProperties": false,++ "required": ["kind", "name", "status", "reason"],++ "properties": {++ "id": { "type": "string", "minLength": 1 },++ "principal_id": { "type": "string", "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$" },++ "kind": { "$ref": "#/$defs/kind" },++ "name": { "type": "string", "minLength": 1 },++ "status": { "$ref": "#/$defs/status" },++ "reason": { "type": ["string", "null"] },++ "price": { "$ref": "#/$defs/price" },++ "quoted_at": { "type": "string", "format": "date-time" },++ "expires_at": { "type": "string", "format": "date-time" }++ },++ "if": { "properties": { "kind": { "const": "exchange" }, "status": { "const": "ready" } }, "required": ["kind", "status"] },++ "then": { "required": ["price", "quoted_at", "expires_at"], "properties": { "price": { "type": "object" } } }++ },++ "candidate": {++ "allOf": [++ { "$ref": "#/$defs/source" },++ { "type": "object", "required": ["id", "price"], "properties": { "id": { "type": "string", "minLength": 1 } } }++ ]++ }++ },++ "examples": [++ {++ "sources": [++ { "kind": "personal", "name": "Your Link machines", "status": "ready", "reason": null },++ { "kind": "cloud", "name": "OpenVibe cloud machines", "status": "coming_soon", "reason": "No cloud pool is accepting jobs yet." },++ { "kind": "exchange", "name": "Exchange GPU rentals", "status": "coming_soon", "reason": "A live quote is required.", "price": null },++ { "kind": "community", "name": "Community machines", "status": "coming_soon", "reason": "Shared placement is being connected." }++ ],++ "candidates": [++ { "id": "dev_01J0000000000000000000000", "kind": "personal", "name": "Studio", "status": "ready", "reason": null, "price": { "amount_usd": 0, "unit": "hour" } }++ ]++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/network/link-list-result.v1.json",++ "title": "NetworkLinkListResult",++ "description": "network.link-list-result@1: GET /api/v1/links on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().",++ "type": "object",++ "required": ["nodes", "generated_at"],++ "additionalProperties": false,++ "properties": {++ "nodes": { "type": "array", "items": { "$ref": "link.v1.json" } },++ "generated_at": { "type": "string", "format": "date-time" }++ }++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/network/link-report-request.v1.json",++ "title": "NetworkLinkReportRequest",++ "description": "network.link-report-request@1: POST /internal/links/report on OpenVibe.Network (capability network.link.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.",++ "type": "object",++ "required": ["source", "nodes"],++ "additionalProperties": false,++ "properties": {++ "source": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$", "description": "The reporting inventory (e.g. the host id)." },++ "nodes": { "type": "array", "maxItems": 500, "items": { "$ref": "link.v1.json" } }++ }++}nnew file mode 100644@@@ -0,0 +1,42 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/network/link.v1.json",++ "title": "NetworkLink",++ "description": "network.link@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.",++ "type": "object",++ "required": ["id", "roles", "location", "health", "updated_at"],++ "additionalProperties": false,++ "properties": {++ "id": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$" },++ "name": { "type": "string", "maxLength": 80 },++ "roles": {++ "type": "array", "minItems": 1, "uniqueItems": true,++ "items": { "enum": ["web", "app", "data", "media-worker", "ingest", "edge-probe", "edge-relay", "edge-cache", "gpu", "staging", "ci"] }++ },++ "location": {++ "type": "object",++ "required": ["region", "country"],++ "additionalProperties": false,++ "properties": {++ "region": { "type": "string", "pattern": "^[a-z]{2}-[a-z]+(-[0-9])?$", "description": "e.g. us-west, eu-central" },++ "country": { "type": "string", "pattern": "^[A-Z]{2}$" },++ "city": { "type": "string", "maxLength": 80 },++ "lat": { "type": "number", "minimum": -90, "maximum": 90 },++ "lon": { "type": "number", "minimum": -180, "maximum": 180 }++ }++ },++ "provider": { "type": "string", "maxLength": 40 },++ "beacon": { "type": "string", "format": "uri", "pattern": "^https://", "description": "Timed by clients (GET, tiny body, no caching) to measure round-trip time." },++ "health": {++ "type": "object",++ "required": ["status", "checked_at"],++ "additionalProperties": false,++ "properties": {++ "status": { "enum": ["up", "degraded", "down", "unknown"] },++ "checked_at": { "type": "string", "format": "date-time" }++ }++ },++ "updated_at": { "type": "string", "format": "date-time" },++ "cell": { "type": "string", "pattern": "^[a-z]{2,8}-[0-9]{1,3}$", "description": "Cell the node belongs to, e.g. wnam-1." }++ }++}@@@ -2,7 +2,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/network/machine-pairing.v1.json","title": "NetworkMachinePairing",-- "description": "network.machine-pairing@1: a one-time code that pairs a machine with the person's account (POST /api/v1/machines/pairings, capability machines.manage). The code is shown once, expires, and works once: the machine redeems it and becomes the person's. The command is what the owner runs on the machine (openvibe.bot/install). The code is never stored in the clear and never returned again.",++ "description": "network.machine-pairing@1: a one-time code that pairs a machine with the person's account (POST /api/v1/machines/pairings, capability machines.manage). The code is shown once, expires, and works once: the machine redeems it and becomes the person's. The command is what the owner runs on the machine (openvibe.link/install). The code is never stored in the clear and never returned again.","type": "object","required": ["code", "expires_at", "installer"],"additionalProperties": false,@@@ -21,7 +21,7 @@"installer": {"type": "string","maxLength": 500,-- "description": "The command the owner runs on the machine (openvibe.bot/install, with the network, the pairing id and the code)."++ "description": "The command the owner runs on the machine (openvibe.link/install, with the network, the pairing id and the code)."}}}@@@ -14,8 +14,8 @@},"principal_id": {"type": "string",-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "The machine's node principal on Network (nod_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for."++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The machine's node principal on Network (lnk_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for."},"name": { "type": "string", "maxLength": 80, "description": "The name the owner gave it. Absent when they gave none." },"kind": {ddeleted file mode 100644@@@ -1,13 +0,0 @@--{-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/network/node-list-result.v1.json",-- "title": "NetworkNodeListResult",-- "description": "network.node-list-result@1: GET /api/v1/nodes on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().",-- "type": "object",-- "required": ["nodes", "generated_at"],-- "additionalProperties": false,-- "properties": {-- "nodes": { "type": "array", "items": { "$ref": "node.v1.json" } },-- "generated_at": { "type": "string", "format": "date-time" }-- }--}ddeleted file mode 100644@@@ -1,13 +0,0 @@--{-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/network/node-report-request.v1.json",-- "title": "NetworkNodeReportRequest",-- "description": "network.node-report-request@1: POST /internal/nodes/report on OpenVibe.Network (capability network.node.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.",-- "type": "object",-- "required": ["source", "nodes"],-- "additionalProperties": false,-- "properties": {-- "source": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$", "description": "The reporting inventory (e.g. the host id)." },-- "nodes": { "type": "array", "maxItems": 500, "items": { "$ref": "node.v1.json" } }-- }--}ddeleted file mode 100644@@@ -1,42 +0,0 @@--{-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/network/node.v1.json",-- "title": "NetworkNode",-- "description": "network.node@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.",-- "type": "object",-- "required": ["id", "roles", "location", "health", "updated_at"],-- "additionalProperties": false,-- "properties": {-- "id": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$" },-- "name": { "type": "string", "maxLength": 80 },-- "roles": {-- "type": "array", "minItems": 1, "uniqueItems": true,-- "items": { "enum": ["web", "app", "data", "media-worker", "ingest", "edge-probe", "edge-relay", "edge-cache", "gpu", "staging", "ci"] }-- },-- "location": {-- "type": "object",-- "required": ["region", "country"],-- "additionalProperties": false,-- "properties": {-- "region": { "type": "string", "pattern": "^[a-z]{2}-[a-z]+(-[0-9])?$", "description": "e.g. us-west, eu-central" },-- "country": { "type": "string", "pattern": "^[A-Z]{2}$" },-- "city": { "type": "string", "maxLength": 80 },-- "lat": { "type": "number", "minimum": -90, "maximum": 90 },-- "lon": { "type": "number", "minimum": -180, "maximum": 180 }-- }-- },-- "provider": { "type": "string", "maxLength": 40 },-- "beacon": { "type": "string", "format": "uri", "pattern": "^https://", "description": "Timed by clients (GET, tiny body, no caching) to measure round-trip time." },-- "health": {-- "type": "object",-- "required": ["status", "checked_at"],-- "additionalProperties": false,-- "properties": {-- "status": { "enum": ["up", "degraded", "down", "unknown"] },-- "checked_at": { "type": "string", "format": "date-time" }-- }-- },-- "updated_at": { "type": "string", "format": "date-time" },-- "cell": { "type": "string", "pattern": "^[a-z]{2,8}-[0-9]{1,3}$", "description": "Cell the node belongs to, e.g. wnam-1." }-- }--}@@@ -117,7 +117,7 @@{"type": "object","additionalProperties": false,-- "description": "Device to server: wall-clock second `second` of the job has fully elapsed. Metering (platform.usage-sample@1, written through billing.usage.record by whoever terminates the link: OpenVibe.Bot now, Fabric later): second n is [started_ms + n*1000, started_ms + (n+1)*1000) on the worker's monotonic clock. A worker sends `job_usage` for n only once that second has fully elapsed, so every `job_usage` stands for quantity 1; the partial last second is never sent here, only in `job_exit`. Each second is exactly one reading: `id` and `idempotency_key` `run:<job id>:<n>`, `service` `run`, `operation` `function.invoke`, `unit` `s`, `quantity` 1 (the partial last second: (wall_ms mod 1000)/1000, never 0), `at` new Date(started_ms + n*1000).toISOString(), `resource` the job id, `node` the worker's device id, `source` `openvibe-node.worker`, and `project`, `subject` and `provider` from the dispatcher's own record of the job. No field depends on when or how often a frame was sent, and no rating fields (`cost_estimate`, `free_allowance_used`, `vibes_charged`) are set, because Billing keeps the first reading under a key, replays an identical one and refuses a different one (409): a resend after a reconnect, a `job_usage` and the `job_exit` backfill of the same second, and two writers of one job all land on one record.",++ "description": "Device to server: wall-clock second `second` of the job has fully elapsed. Metering (platform.usage-sample@1, written through billing.usage.record by whoever terminates the link: OpenVibe.Bot now, Fabric later): second n is [started_ms + n*1000, started_ms + (n+1)*1000) on the worker's monotonic clock. A worker sends `job_usage` for n only once that second has fully elapsed, so every `job_usage` stands for quantity 1; the partial last second is never sent here, only in `job_exit`. Each second is exactly one reading: `id` and `idempotency_key` `run:<job id>:<n>`, `service` `run`, `operation` `function.invoke`, `unit` `s`, `quantity` 1 (the partial last second: (wall_ms mod 1000)/1000, never 0), `at` new Date(started_ms + n*1000).toISOString(), `resource` the job id, `node` the worker's device id, `source` `openvibe-link.worker`, and `project`, `subject` and `provider` from the dispatcher's own record of the job. No field depends on when or how often a frame was sent, and no rating fields (`cost_estimate`, `free_allowance_used`, `vibes_charged`) are set, because Billing keeps the first reading under a key, replays an identical one and refuses a different one (409): a resend after a reconnect, a `job_usage` and the `job_exit` backfill of the same second, and two writers of one job all land on one record.","required": ["v","seq",nnew file mode 100644@@@ -0,0 +1,194 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/platform/link-capabilities.v1.json",++ "title": "LinkCapabilities",++ "description": "Link machine hardware, placement regions, tags, and operating costs (T1 Universal Fabric; ADR-034 proposed).",++ "$comment": "Additive optional fields may extend this v1 contract.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "node_id",++ "cpu",++ "arch",++ "memory_mb",++ "storage",++ "network",++ "regions",++ "tags",++ "costs"++ ],++ "properties": {++ "node_id": {++ "type": "string"++ },++ "cpu": {++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "cores": {++ "type": "number",++ "minimum": 0++ },++ "threads": {++ "type": "integer",++ "minimum": 0++ }++ },++ "required": [++ "cores"++ ]++ },++ "arch": {++ "type": "string",++ "description": "CPU architecture"++ },++ "memory_mb": {++ "type": "integer",++ "minimum": 0++ },++ "gpu": {++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "model": {++ "type": "string"++ },++ "count": {++ "type": "integer",++ "minimum": 0++ },++ "vram_mb": {++ "type": "integer",++ "minimum": 0++ }++ },++ "required": [++ "model",++ "count"++ ]++ },++ "storage": {++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "capacity_gb": {++ "type": "number",++ "minimum": 0++ },++ "available_gb": {++ "type": "number",++ "minimum": 0++ },++ "kind": {++ "type": "string"++ }++ },++ "required": [++ "capacity_gb"++ ]++ },++ "network": {++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "ingress_mbps": {++ "type": "number",++ "minimum": 0++ },++ "egress_mbps": {++ "type": "number",++ "minimum": 0++ }++ },++ "required": [++ "ingress_mbps",++ "egress_mbps"++ ]++ },++ "regions": {++ "type": "array",++ "items": {++ "type": "string"++ },++ "minItems": 1++ },++ "tags": {++ "type": "array",++ "items": {++ "type": "string"++ }++ },++ "costs": {++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "per_hour_usd": {++ "type": "number",++ "minimum": 0,++ "description": "Estimated USD"++ },++ "egress_per_gb_usd": {++ "type": "number",++ "minimum": 0,++ "description": "Estimated USD"++ }++ },++ "required": [++ "per_hour_usd"++ ]++ },++ "capabilities": {++ "type": "array",++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"++ },++ "description": "What the machine offers, in the platform.resource-offer@1 capability form (e.g. node:http, worker:ffmpeg)."++ },++ "agent_version": {++ "type": "string",++ "maxLength": 40,++ "description": "Version of the node agent that reported."++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ }++ },++ "examples": [++ {++ "node_id": "node-1",++ "cpu": {++ "cores": 8,++ "threads": 16++ },++ "arch": "x86_64",++ "memory_mb": 16384,++ "gpu": {++ "model": "L4",++ "count": 1,++ "vram_mb": 24576++ },++ "storage": {++ "capacity_gb": 500,++ "available_gb": 300,++ "kind": "ssd"++ },++ "network": {++ "ingress_mbps": 1000,++ "egress_mbps": 1000++ },++ "regions": [++ "us-west"++ ],++ "tags": [++ "gpu",++ "edge"++ ],++ "costs": {++ "per_hour_usd": 0.2,++ "egress_per_gb_usd": 0.01++ }++ }++ ]++}ddeleted file mode 100644@@@ -1,194 +0,0 @@--{-- "$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/platform/node-capabilities.v1.json",-- "title": "NodeCapabilities",-- "description": "Node hardware, placement regions, tags, and operating costs (T1 Universal Fabric; ADR-034 proposed).",-- "$comment": "Additive optional fields may extend this v1 contract.",-- "type": "object",-- "additionalProperties": false,-- "required": [-- "node_id",-- "cpu",-- "arch",-- "memory_mb",-- "storage",-- "network",-- "regions",-- "tags",-- "costs"-- ],-- "properties": {-- "node_id": {-- "type": "string"-- },-- "cpu": {-- "type": "object",-- "additionalProperties": false,-- "properties": {-- "cores": {-- "type": "number",-- "minimum": 0-- },-- "threads": {-- "type": "integer",-- "minimum": 0-- }-- },-- "required": [-- "cores"-- ]-- },-- "arch": {-- "type": "string",-- "description": "CPU architecture"-- },-- "memory_mb": {-- "type": "integer",-- "minimum": 0-- },-- "gpu": {-- "type": "object",-- "additionalProperties": false,-- "properties": {-- "model": {-- "type": "string"-- },-- "count": {-- "type": "integer",-- "minimum": 0-- },-- "vram_mb": {-- "type": "integer",-- "minimum": 0-- }-- },-- "required": [-- "model",-- "count"-- ]-- },-- "storage": {-- "type": "object",-- "additionalProperties": false,-- "properties": {-- "capacity_gb": {-- "type": "number",-- "minimum": 0-- },-- "available_gb": {-- "type": "number",-- "minimum": 0-- },-- "kind": {-- "type": "string"-- }-- },-- "required": [-- "capacity_gb"-- ]-- },-- "network": {-- "type": "object",-- "additionalProperties": false,-- "properties": {-- "ingress_mbps": {-- "type": "number",-- "minimum": 0-- },-- "egress_mbps": {-- "type": "number",-- "minimum": 0-- }-- },-- "required": [-- "ingress_mbps",-- "egress_mbps"-- ]-- },-- "regions": {-- "type": "array",-- "items": {-- "type": "string"-- },-- "minItems": 1-- },-- "tags": {-- "type": "array",-- "items": {-- "type": "string"-- }-- },-- "costs": {-- "type": "object",-- "additionalProperties": false,-- "properties": {-- "per_hour_usd": {-- "type": "number",-- "minimum": 0,-- "description": "Estimated USD"-- },-- "egress_per_gb_usd": {-- "type": "number",-- "minimum": 0,-- "description": "Estimated USD"-- }-- },-- "required": [-- "per_hour_usd"-- ]-- },-- "capabilities": {-- "type": "array",-- "items": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"-- },-- "description": "What the machine offers, in the platform.resource-offer@1 capability form (e.g. node:http, worker:ffmpeg)."-- },-- "agent_version": {-- "type": "string",-- "maxLength": 40,-- "description": "Version of the node agent that reported."-- },-- "updated_at": {-- "type": "string",-- "format": "date-time"-- }-- },-- "examples": [-- {-- "node_id": "node-1",-- "cpu": {-- "cores": 8,-- "threads": 16-- },-- "arch": "x86_64",-- "memory_mb": 16384,-- "gpu": {-- "model": "L4",-- "count": 1,-- "vram_mb": 24576-- },-- "storage": {-- "capacity_gb": 500,-- "available_gb": 300,-- "kind": "ssd"-- },-- "network": {-- "ingress_mbps": 1000,-- "egress_mbps": 1000-- },-- "regions": [-- "us-west"-- ],-- "tags": [-- "gpu",-- "edge"-- ],-- "costs": {-- "per_hour_usd": 0.2,-- "egress_per_gb_usd": 0.01-- }-- }-- ]--}@@@ -2,7 +2,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/platform/resource-offer.v1.json","title": "ResourceOffer",-- "description": "What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.node@1) deliberately leaves out.",++ "description": "What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.link@1) deliberately leaves out.","type": "object","additionalProperties": false,"required": [@@@ -33,7 +33,7 @@},"node_id": {"type": "string",-- "description": "network.node@1 id, for kind node"++ "description": "network.link@1 id, for kind node"},"provider": {"type": "string",@@@ -71,7 +71,7 @@"type": "string","pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"},-- "description": "e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2. 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 Link'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. Beside them, a kind harness names `runtime:<class>` for each platform.runtime-class@1 class it can run (runtime:function, runtime:code, runtime:browser, runtime:linux, runtime:desktop, runtime:gpu); these are ordinary capability names, not reserved, and OpenVibe.Codes already advertises the classes it offers."++ "description": "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 Link's job worker, one per class: worker:function, worker:code, worker:browser, worker:linux, worker:desktop and worker:gpu ($defs.reservedWorkerCapabilities). A Link advertises the name of each class it runs (Link's status.capabilities.worker); every other `worker:` name (worker:ffmpeg, worker:ai-gpu) is an ordinary capability name. Beside them, a kind harness names `runtime:<class>` for each platform.runtime-class@1 class it can run (runtime:function, runtime:code, runtime:browser, runtime:linux, runtime:desktop, runtime:gpu); these are ordinary capability names, not reserved, and OpenVibe.Codes already advertises the classes it offers."},"capacity": {"type": "object",@@@ -146,7 +146,7 @@"quantity": 1,"unit": "s","at": "2026-10-02T10:00:00.250Z",-- "source": "openvibe-node.worker"++ "source": "openvibe-link.worker"}]}@@@ -132,7 +132,7 @@"required": ["node", "provider", "region"],"properties": {"node": { "type": "string", "minLength": 1, "maxLength": 128, "description": "The worker's device id (the usage reading's `node`)." },-- "provider": { "type": ["string", "null"], "maxLength": 128, "description": "The offer's provider (the usage reading's `provider`); null for a first-party node." },++ "provider": { "type": ["string", "null"], "maxLength": 128, "description": "The offer's provider (the usage reading's `provider`); null for a first-party machine." },"region": { "type": ["string", "null"], "maxLength": 64 }}},@@@ -2,7 +2,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/watch/watch.v1.json","title": "Watch",-- "description": "watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Node probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.",++ "description": "watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Link probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.","type": "object","additionalProperties": false,"required": [@@@ -17,7 +17,7 @@},"node_id": {"type": "string",-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$","description": "Must equal the node principal in the Network token; a mismatch is refused and audited."},"tunnel_token": {@@@ -52,8 +52,8 @@},"node_id": {"type": "string",-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "Optional: bind the share to this Node principal now. Omit to bind it when the first tunnel connects."++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "Optional: bind the share to this Link principal now. Omit to bind it when the first tunnel connects."},"region": {"type": "string",@@@ -2,7 +2,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/website/share.v1.json","title": "WebsiteShare",-- "description": "website.share@1: one Share on OpenVibe.Website Connect, a local port published at a stable hostname through the edge relay. The record says who owns it, which project scopes it, which Node tunnel serves it, which local target it may reach, which policy version the edge enforces and where it is in its life (draft, review, active, paused, revoked). The tunnel token, the request bodies the inspector captures and the policy's password hash are never part of this document.",++ "description": "website.share@1: one Share on OpenVibe.Website Connect, a local port published at a stable hostname through the edge relay. The record says who owns it, which project scopes it, which Link tunnel serves it, which local target it may reach, which policy version the edge enforces and where it is in its life (draft, review, active, paused, revoked). The tunnel token, the request bodies the inspector captures and the policy's password hash are never part of this document.","type": "object","additionalProperties": false,"required": [@@@ -44,7 +44,7 @@"string","null"],-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$","description": "The Network node principal expected to serve the tunnel; null until one is bound."},"name": {@@@ -122,7 +122,7 @@"null"],"format": "date-time",-- "description": "The last successful Node liveness check; null until the first tunnel."++ "description": "The last successful Link liveness check; null until the first tunnel."},"created_at": {"type": "string",@@@ -37,7 +37,7 @@ service id); **Runtime** is `STATUS.json.runtime`; **Database** is inferred from| OpenVibe.MediaHub | Video | video.*, pics.*, download.*, mediahub.* | node 22 | PostgreSQL | openvibe.video, openvibe.download, openvibe.pics, openvibe.network | contracts v0.222.0, sdk v0.67.0, shared v4.17.0 | deployed | live | W20 || OpenVibe.Network | Network | identity.*, network.*, theme.*, notification.* | node 22 | PostgreSQL | openvibe.network, auth.openvibe.network, api.openvibe.network, admin.openvibe.network, my.openvibe.network, themes.openvibe.network | contracts v0.222.0, sdk v0.67.0, shared v4.17.0 | deployed | live | — || OpenVibe.News | News | news.* | node 22 | PostgreSQL | openvibe.news | contracts v0.222.0, sdk v0.67.0, shared v4.17.0, publishing v1.4.1 | deployed | live | W17 |--| OpenVibe.Node | — | — | go 1.24 (one static binary per platform) | none | — | — | not deployed | — | T15 |++| OpenVibe.Link | — | — | go 1.24 (one static binary per platform) | none | — | — | not deployed | — | T15 || OpenVibe.Play | Play | play.* | node 22 | PostgreSQL | play.openvibe.network | contracts v0.222.0, sdk v0.67.0, shared v4.17.0 | deployed | live | T8 || OpenVibe.Publishing | OpenVibe.Publishing | publishing | node >=20 | none | — | contracts v0.222.0, sdk v0.67.0, shared v4.17.0 | not deployed | library | W15 || OpenVibe.Quest | Quest | quest.* | node 22 | PostgreSQL | openvibe.quest | contracts v0.222.0, sdk v0.67.0, shared v4.17.0, publishing v1.4.0 | deployed | live | W20 |@@@ -66,7 +66,7 @@ OpenVibe.Tools that day found:### Browser-only tools may get server engines--A pure transform whose engine also runs in Node gets an opt-in server engine for the API: JSON,++A pure transform whose engine also runs in Link gets an opt-in server engine for the API: JSON,minify and beautify, case, slug, hash, base64 and the like. Its descriptor then says`execution: client` and `api: true`. The page stays browser-only, so "nothing leaves your browser"stays true for people using the page. A tool without such an engine says `api: false`.@@@ -19,7 +19,7 @@ What exists already maps onto the primitives of a cloud:| Developer surface | Codes (portal, apps, playgrounds, docs), openvibe-sdk v0.13, Contracts v0.75 | ADR-002, ADR-008 || Extensions | Mods with their own principals, budgets and grants; Tools platform API | ADR-013, ADR-027 |--Everything runs on one host: about 25 Node services, one SQLite database each, as systemd units. Measured load is about 1% of what SQLite handles (ADR-007 amendment, 2026-09-26). Deploys are manual `ovhost deploy` runs.++Everything runs on one host: about 25 Link services, one SQLite database each, as systemd units. Measured load is about 1% of what SQLite handles (ADR-007 amendment, 2026-09-26). Deploys are manual `ovhost deploy` runs.At a much larger size, the database engine and the orchestrator are not what is expensive to change. The expensive things are **boundaries**: how tenants are separated, how resources are named, how access is decided, what the API contract is, and how usage is counted. Each of those touches every row, every route and every event, so changing it late means migrating everything. Today they are cheap to set.@@@ -9,7 +9,7 @@ isolation belongs to Stage C) and ADR-046 (offers, requirements, placement, sign- **Plan T14** (lines 559–583): *"Finish line: any workload — a function, code, a browser session, a Linux or desktopmachine, a GPU job — runs in a sandbox with metering, limits, cancellation and streaming, placed by the Fabric on-- the cheapest capability that works; the Node runtime runs the same workloads on official servers and on users' own++ the cheapest capability that works; the Link runtime runs the same workloads on official servers and on users' ownmachines under their trust policy."* And: *"**Run is not a Host detail.** Host deploys persistent applications; Runexecutes isolated jobs/environments. They can share machines; they are separate authorities (ADR-036 rewrittenaround this expanded purpose)."*@@@ -77,16 +77,16 @@ Run states `platform.workload-requirements@1` and reads the `platform.placement-Run writes no placer of its own (ADR-046: "A service states requirements and reads results; it does not rankoffers."). The plan's §2.1.11 cross-reference is the Fabric section as written in ADR-046/T1, not a separate scheme.--**Node descriptor → offers.** When Run turns an OpenVibe Link into Fabric offers it applies the mapping the Node++**Link descriptor → offers.** When Run turns an OpenVibe Link into Fabric offers it applies the mapping the Linkprotocol already fixes (OpenVibe Link `docs/protocol.md:310–321`):--| Node descriptor field | Fabric offer | Example |++| Link descriptor field | Fabric offer | Example || --- | --- | --- || `capabilities.worker.runtime_classes` | `resource-offer.capabilities` item `worker:<class>` | `worker:function`, `worker:code` || `capabilities.worker.max_jobs` | `runtime-offer.limits.concurrency` | 1 || `capabilities.worker.max_ttl_ms` | `runtime-offer.limits.duration_seconds` | 600 || device `node_id` / cell | `resource-offer.node_id` / `resource-offer.cell` | `node_01H…` / `wnam-1` |--| worker trust (local policy) | `resource-offer.trust` | `user-owned` for a user's own Node |++| worker trust (local policy) | `resource-offer.trust` | `user-owned` for a user's own Link |### 4. The cost ladder@@@ -130,7 +130,7 @@ Every job, on every adapter, is held to the same guarantees (plan T14, lines 5721. **Isolation mechanism.** The plan requires more than a container for hostile arbitrary code but names nomechanism. Micro-VM (Firecracker/Kata), a userspace kernel (gVisor) and namespace/seccomp sandboxes are all open;the Link worker's current `function` isolation is namespaces plus a seccomp allowlist.--2. **Snapshot/resume backend.** Whether a resumed environment's state lives in Media, a Node-local disk, or a++2. **Snapshot/resume backend.** Whether a resumed environment's state lives in Media, a Link-local disk, or aprovider's own snapshot is open.3. **Idle-suspend policy.** Who decides to suspend — Run, the node's local policy, or a placement objective — isopen.@@@ -147,6 +147,6 @@ Every job, on every adapter, is held to the same guarantees (plan T14, lines 572`linux`, `desktop` and `gpu` are reserved names with no worker.- Cells are a single host (`cell = wnam-1`) today; O24's second physical cell, the WireGuard mesh and geo placementare not built (plan T14, lines 580–581).--- Per-second metering is proven only for Node jobs; no Billing consumer reads Run usage on `origin/main` yet.++- Per-second metering is proven only for Link jobs; no Billing consumer reads Run usage on `origin/main` yet.- The provider adapters above are the plan's list, not deployed adapters; no managed-browser, external-sandbox orexternal-GPU offer producer exists.@@@ -39,7 +39,7 @@ a Cozmo-based robot).robot and reaches it over the robot's own link (Cozmo first through PyCozmo; later Vector, Tello, Sphero, LEGO); abridge joins the robot's network on one interface and reaches OpenVibe on another. **Server-side**: Bot itselfconnects to ONVIF/RTSP cameras (credentials by secret reference, never in the profile). The agent is a Python 3 package-- (the Pi, PyCozmo and the kit libraries are Python), installed by `curl -fsSL https://openvibe.bot/install | sh` into a++ (the Pi, PyCozmo and the kit libraries are Python), installed by `curl -fsSL https://openvibe.link/install | sh` into avirtual environment with a systemd unit and the hardware watchdog.4. **Control runs over one outbound WebSocket per device; video over WHIP.** The device dials Bot(`wss://openvibe.bot/device`), authenticates with its credential, and keeps one connection for commands, telemetry and@@@ -6,7 +6,7 @@ the first new adapters (OpenCode, Antigravity CLI), capability-aware routing, vi(headless browser + virtual desktop, screenshots)" ahead of "ADR-044 (task contract, adapter interface, modes,signals, verification, trust)", and the engine's harness work is paused where it stands. Contract: `platform.task@1`.Builds on ADR-034 §2 (projects and resource names), §5 (control and data plane) and §9 (metering), on ADR-046 (the--placer, its objectives and the five trust classes), on ADR-012 (one Billing authority) and on ADR-043 (Node control++placer, its objectives and the five trust classes), on ADR-012 (one Billing authority) and on ADR-043 (Link controllevels and the local kill switch). For the owner's review.## Context and current evidence@@@ -112,7 +112,7 @@ ADR-046 §4's five classes apply unchanged: `first-party`, `user-owned`, `partnedata on OpenVibe — `first-party`, and a `user-owned` node only when the deployment allows it — which the plandescribes as "only agents that keep your data on OpenVibe: its own runtime and open models on its hardware". Localexecution on a person's own machine goes through OpenVibe Link under ADR-043's control levels (Observe / Ask /--Trusted / Full control), a local indicator and a kill switch the owner holds; Node's local policy always overrides a++Trusted / Full control), a local indicator and a kill switch the owner holds; Link's local policy always overrides acloud command (plan T14).### 7. Progress, cancel and webhooks@@@ -146,7 +146,7 @@ Actor plans and routes; it does not become an authority. The plan's own list of| Coding work | Codes (the one coding-harness router) || Persistent conditions | Watch || Cloud execution | Run (`platform.job@1` / `platform.job-frame@1`) |--| Local execution and computer control | Node (ADR-043 control levels, kill switch) |++| Local execution and computer control | Link (ADR-043 control levels, kill switch) || Resource control | Services (`common.resource-control-request@1`, ADR-048) || Artifacts | Media || Robots | Bot |@@@ -1,26 +1,26 @@# ADR-045: The Link is OpenVibe's universal runtime--**Status:** Proposed 2026-10-05 (plan §8: *"ADR-045 (Node; **revise: Node is the universal runtime, not a++**Status:** Proposed 2026-10-05 (plan §8: *"ADR-045 (Link; **revise: Link is the universal runtime, not aBot/Actor helper**)"*; plan T14, lines 575–579). For the owner's review. Builds on ADR-043 (Bot — devices, pairing,control and safety), ADR-046 §5 (the `worker:<class>` capability names) and ADR-048 (OVRN and authority boundaries).--ADR-043 stays the robot/device half of the same Node; this record is the runtime half, and amends ADR-043's credential++ADR-043 stays the robot/device half of the same Link; this record is the runtime half, and amends ADR-043's credentialparagraph: Network owns the node/device principal and the pairing credential, Bot keeps the robot binding and controllink.## Context and current evidence--- **The plan revises ADR-045.** Plan §8 (line 986) calls ADR-045 written but to be revised: *"Node is the universal++- **The plan revises ADR-045.** Plan §8 (line 986) calls ADR-045 written but to be revised: *"Link is the universalruntime, not a Bot/Actor helper"*. `origin/main` has no ADR-045 file in any repository, so the revision is unstatedin the contract repository.--- **What the plan says the Link is.** Plan T14, lines 575–579: *"**Node runtime** (ADR-045 revised: Node is the++- **What the plan says the Link is.** Plan T14, lines 575–579: *"**Link runtime** (ADR-045 revised: Link is theuniversal runtime, not a Bot/Actor helper). **Core built on `origin/main`:** one signed binary, pairing, controllink, WHIP publisher, plugin runtime, the Adeept and Cozmo plugins, installer and CI. Still to add for its otherroles: computer control (terminal, files, screen, input, browser) under control levels, local kill switch andrecording (Actor's need, T17); execution-worker capability (Run's need); cache/storage/delivery capability (Media'sneed, T4). **Local policy always overrides cloud commands.**"*--- **Products bind capabilities, not device code.** Plan lines 321–324: *"Node identity is a Network principal.-- Network owns the node/device principal and pairing credentials. Services manages the resource; Node presents-- capabilities (T14); Bot binds robot resources to Node capabilities (T15); Actor binds computer control (T17); Run++- **Products bind capabilities, not device code.** Plan lines 321–324: *"Link identity is a Network principal.++ Network owns the node/device principal and pairing credentials. Services manages the resource; Link presents++ capabilities (T14); Bot binds robot resources to Link capabilities (T15); Actor binds computer control (T17); Runbinds execution workers (T14); Media binds cache/storage (T4). Bot keeps robots, robot profiles, operators, commandleases and physical safety — not universal device identity."*- **What the code already does** (OpenVibe Link, `origin/main`): one Go static binary per platform — pairing@@@ -35,20 +35,20 @@ link.- **The capability names are already agreed.** ADR-046 §5 reserves `worker:function`, `worker:code`,`worker:browser`, `worker:linux`, `worker:desktop` and `worker:gpu` for the Link job worker, one per`platform.runtime-class@1` class, in `platform.resource-offer@1.$defs.reservedWorkerCapabilities`.--- **The control levels are plan wording.** Plan lines 745–747: *"each Node has a control level set on the device++- **The control levels are plan wording.** Plan lines 745–747: *"each Link has a control level set on the device(Observe / Ask / Trusted / Full control), a local indicator, a local kill switch, optional container/VMconfinement, replayable transcripts and file changes, and local secrets used through handles."* Of that, the localkill switch (the persisted stop latch) exists; the four named levels and recording do not.--- **Media's need is stated too.** Plan lines 366–368: *"Node is a storage/delivery candidate too++- **Media's need is stated too.** Plan lines 366–368: *"Link is a storage/delivery candidate too(`media.cache`, `storage.hot`, prepaid disk and bandwidth), so the placement engine can use unused capacity on-- already-paid OpenVibe nodes before buying CDN traffic."*++ already-paid OpenVibe Links before buying CDN traffic."*## Decision--### 1. One Node, the universal runtime++### 1. One Link, the universal runtimeThe Link is OpenVibe's universal runtime — one agent for a person's computers, servers, Raspberry Pis, phones and--robots. It is **not** a Bot helper or an Actor helper: Bot, Actor, Run and Media are all clients of the same Node,++robots. It is **not** a Bot helper or an Actor helper: Bot, Actor, Run and Media are all clients of the same Link,and each binds the capabilities it needs. ADR-043 remains the robot/device half (robots, robot profiles, operators,command leases and physical safety); this ADR is the runtime half. The one split this record draws inside ADR-043 isthe credential: **Network owns the node/device principal and issues the pairing credential; Bot keeps the robot@@@ -61,7 +61,7 @@ stands.A role is **a capability the Link advertises plus local configuration**, never a second agent or a second install.- The Link is a Network principal (its node/device principal and pairing credential are Network's), and its resourceis named by OVRN (ADR-048). Network owns identity and pairing; Bot keeps the robot binding and the control link-- (amending ADR-043's credential paragraph); Node presents capabilities.++ (amending ADR-043's credential paragraph); Link presents capabilities.### 3. Execution worker (Run's need)@@@ -102,7 +102,7 @@ stands.## Alternatives considered--- **Node as a Bot-only helper (the plan's earlier framing):** rejected by the revision. Bot, Actor, Run and Media++- **Link as a Bot-only helper (the plan's earlier framing):** rejected by the revision. Bot, Actor, Run and Mediawould each grow a device agent, and an owner would install and pair several.- **A separate agent per product (an Actor agent, a Run worker, a Media cache daemon):** rejected. One binary, onecontrol link and one local policy are what make a single device safe to reason about; capabilities distinguish the@@@ -128,7 +128,7 @@ stands.2. **One principal or many.** Whether a Link serving several products/projects is one Network principal with scopedgrants or one principal per project is Network's decision and is open (ADR-048 OVRN).3. **Cache/storage contracts.** Which offer contracts carry the Link's cache/storage role — the existing-- `platform.storage-offer@1` / `platform.delivery-offer@1` or a Node-specific one — and whether placement policy++ `platform.storage-offer@1` / `platform.delivery-offer@1` or a Link-specific one — and whether placement policystays Media's, is open.4. **Other users' workloads.** Whether a Link may run other users' jobs or serve other users' computer control, oronly its owner's, mirrors ADR-046's `user-owned` question and is open.@@@ -140,7 +140,7 @@ stands.- The release binary is not code-signed and has no self-update: releases publish `SHA256SUMS` only.- The four control-level names and the local indicator are plan wording; the code today has a local stop latch, notthe four levels.--- No Actor, Run or Media consumer binds these Node capabilities on `origin/main`; Run is a placeholder service++- No Actor, Run or Media consumer binds these Link capabilities on `origin/main`; Run is a placeholder service(ADR-036).- The Link worker is reached over the Bot control link today; it is not yet an offer producer, because the Run thatwould convert its descriptor is not built.@@@ -77,7 +77,7 @@ no existing caller's eligible set, and the SDK must not add it to its default liAn OpenVibe Link that runs jobs advertises one `worker:<class>` capability in its `resource-offer.capabilities` foreach `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++(submitted code in a sandbox) today, and `worker:browser`, `worker:linux`, `worker:desktop` and `worker:gpu` as Linkgains them. These six names are reserved for the Link 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`.@@@ -18,12 +18,12 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).verdict is one path per pillar: build a focused relay on `quic-go` for Share, adopt headscale + `tsnet` with atenant boundary for Connect, build the policy/abuse layer on top of Cloudflare and Network identity, and extendEvents for hooks. §3.3 and §3.4 name what to build and what not to reuse.--- **The data plane is Go; the control plane is Node.** The Link agent is Go (ADR-045). QUIC, HTTP/3, datagrams,-- half-close and backpressure are native there and awkward in Node.js. The control API can stay Node/Express, like++- **The data plane is Go; the control plane is Link.** The Link agent is Go (ADR-045). QUIC, HTTP/3, datagrams,++ half-close and backpressure are native there and awkward in Node.js. The control API can stay Link/Express, likeevery other network service. The edge is a separate, exposed, bandwidth-heavy tier and must not share a host withthe control plane (research §4.1).--- **Node already has a principal.** Network owns node principals (`nod_…`, `ids.newId('node')`) and pairs machines;-- `network.node.revoked` already exists in Network's manifest. Connect must reuse that principal, not mint a second++- **Link already has a principal.** Network owns node principals (`lnk_…`, `ids.newId('link')`) and pairs machines;++ `network.link.revoked` already exists in Network's manifest. Connect must reuse that principal, not mint a secondmachine identity. Network also owns OIDC, project membership and grants; Domains owns DNS proposals; Events ownsdurable delivery and v2 signatures (ADR-042); Report and Billing consume usage.- **Space already uses `<name>.openvibe.website`.** A single-label name is in use for tenant sites. A new share cannot@@@ -46,19 +46,19 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).## Decision1. **Share is a focused Go edge relay, `openvibe-edge`, plus a Link `connect` core in OpenVibe Link.** The relay is-- built on `quic-go` (MIT): one QUIC connection per Node, ALPN `ovconnect/1`, TLS 1.3, streams for HTTP/WebSocket/TCP,++ built on `quic-go` (MIT): one QUIC connection per Link, ALPN `ovconnect/1`, TLS 1.3, streams for HTTP/WebSocket/TCP,datagrams for UDP, and an HTTP/2 + WebSocket fallback on 443 for networks that block UDP. It routes by SNI/Host to a`share_id` from Connect's name registry, never from a user-controlled header, checks the signed policy, and then-- opens a stream to the Node. It does not own users, shares, policies or audit. No frp, sish, zrok, Pangolin or++ opens a stream to the Link. It does not own users, shares, policies or audit. No frp, sish, zrok, Pangolin orcloudflared code is forked or embedded.--2. **The control API is a new Node service, OpenVibe.Connect.** It owns shares, share policies, devices, mesh policies,++2. **The control API is a new Link service, OpenVibe.Connect.** It owns shares, share policies, devices, mesh policies,hooks, the name registry, abuse holds, audit and quota counters in `ov_connect` PostgreSQL; it mints tunnel tokensand signs the short-lived policy cache the edge reads. It is a peer of Domains, Events, Network, Report and Billing,not a module of Bot. Bot keeps robots, the device link and command safety (ADR-043); Connect keeps shares, the mesh,policies and hooks.3. **Connect mesh is headscale + embedded `tsnet`, with a tenant boundary that must be proven before consumermulti-tenancy.** v1 maps one workspace to one headscale tenant: a person's own network for a personal account, a-- project's network for a team. Node embeds `tsnet` (userspace, no root, no TUN) for the private mesh; the public++ project's network for a team. Link embeds `tsnet` (userspace, no root, no TUN) for the private mesh; the publicingress stays Share. headscale does not implement Funnel or Serve and Connect must not promise Funnel-compatiblebehaviour. The tenant-isolation proof must cover: no cross-tenant MagicDNS resolution, no cross-tenant route orexit-node leak, no shared pre-auth key, and revocation in one tenant leaving the others untouched. If the proof@@@ -68,10 +68,10 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).is for PCs, NAS and servers. DERP is self-hosted (`derper`) so relay traffic stays on OpenVibe infrastructure;Live's TURN is for WebRTC and is not a WireGuard relay.4. **Identity and the tunnel handshake use two credentials, both checked.** The Link presents a Network-- client-credentials token whose subject is its node principal (`node:nod_…`) and whose audience is++ client-credentials token whose subject is its node principal (`link:lnk_…`) and whose audience is`openvibe.website`; the request body is `website.share-connect-request@1` with the `share_id`, the same `node_id`and a short-lived, single-use, share-scoped `tunnel_token` minted by Connect. The edge validates the token against-- Network's JWKS, requires the subject to equal `node:<node_id>` (for example `node:nod_…`), redeems the++ Network's JWKS, requires the subject to equal `link:<node_id>` (for example `link:lnk_…`), redeems thetunnel token with Connect exactly once and binds the connection to that share. One credential is neverenough. The tunnel token is sent once over TLS and isnever stored, logged, audited or published in an event. OIDC at the share host uses Authorization Code + PKCE@@@ -79,10 +79,10 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).`share_id`, `sub` and `policy_version`.`POST /api/v1/shares` returns only `website.share@1`; its response has no tunnel token. The Link obtains theshort-lived, single-use token through a separate authenticated credential exchange before connecting. CON-5-- defines that exchange's endpoint, request and response schemas, authorization, and delivery to the Node.++ defines that exchange's endpoint, request and response schemas, authorization, and delivery to the Link.5. **Revocation is immediate and has one owner per trigger.** `DELETE /api/v1/shares/:id` writes`website.share.revoked`, closes the edge route and tunnel, removes the DNS route if Connect added one and makes the-- public host return 410 within the 5-second budget. `network.node.revoked` closes every share bound to that node.++ public host return 410 within the 5-second budget. `network.link.revoked` closes every share bound to that node.A policy change bumps `policy_version`, so the edge's cached signed policy is stale on the next request. Removing adevice revokes its mesh key at the control plane. No capability, route or cached policy survives a revoked share.6. **Policy is enforced at the edge, authored and audited by Connect, and fronted by Cloudflare.** Cloudflare@@@ -106,8 +106,8 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).Events to retry. Events produces `events.hook.received|delivered|dead`; Connect writes none of them. No smee orHookdeck is adopted.8. **The SDK stays MIT and thin.** `openvibe-sdk` (MIT) gets at most a thin `openvibe-sdk/connect` client that talks-- to the local Node daemon over HTTP/Unix socket and to the control API over HTTP. The QUIC data plane, the mesh-- (`tsnet`) and the policy engine stay in the AGPL Node/Connect/edge code. No AGPL/GPL tunnel code is copied into the++ to the local Link daemon over HTTP/Unix socket and to the control API over HTTP. The QUIC data plane, the mesh++ (`tsnet`) and the policy engine stay in the AGPL Link/Connect/edge code. No AGPL/GPL tunnel code is copied into theSDK or `openvibe-shared` (MIT). This keeps the permissive client boundary and the copyleft in the agent.9. **Naming and the first proof use one label.** The first proof is the owner's agent portal at`https://agents-<owner>.openvibe.website` (single label), behind OpenVibe sign-in, opened on a phone over 5G with a@@@ -127,9 +127,9 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).| Concern | Owner | Not this service ||---|---|---|--| Share record, name registry, policy, tunnel token, inspector metadata, audit | OpenVibe.Connect (Node control API, `ov_connect`) | the edge keeps active session state and a signed policy cache only |++| Share record, name registry, policy, tunnel token, inspector metadata, audit | OpenVibe.Connect (Link control API, `ov_connect`) | the edge keeps active session state and a signed policy cache only || Public QUIC/HTTP/WS/SSE/TCP/UDP ingress, routing, edge policy check, rate limits | `openvibe-edge` (Go, `quic-go`) | it does not own users, shares, policies, audit or hooks |--| Node tunnel, local forwarding, `ov share`, `tsnet` | OpenVibe Link (`internal/connect`) | it does not decide policy or open arbitrary hosts |++| Link tunnel, local forwarding, `ov share`, `tsnet` | OpenVibe Link (`internal/connect`) | it does not decide policy or open arbitrary hosts || Account, node principal, OIDC, grants, revocation event | OpenVibe.Network | Connect never mints identity || DNS proposal, TXT/CNAME, certificate path | OpenVibe.Domains | Connect never writes DNS or holds a provider credential || Durable event store, v2 signature, retries, DLQ, replay | OpenVibe.Events | Connect creates the subscription; the edge does not queue |@@@ -212,7 +212,7 @@ capability id is concrete; `website.abuse.review` stays the moderator queue. Non- `research-2026-10-29/website-connect.md` §1.1–1.7, §3.2–3.4, §4.1–4.13 (checked 2026-10-29).- `OpenVibe_New_Domains_Plan_2026-10-28.md` §5.9b and the CON-1…CON-21 ledger.--- ADR-053 (Website/Host boundary), ADR-045 (Node runtime), ADR-042 (events fabric), ADR-043 (Bot devices), ADR-048++- ADR-053 (Website/Host boundary), ADR-045 (Link runtime), ADR-042 (events fabric), ADR-043 (Bot devices), ADR-048(control plane), ADR-056 (project secrets), ADR-028 (expand/migrate/contract).- `manifests/products/openvibe.website.json` (the distinct product/redirect manifest, preserved), ADR-005 (realtime),`manifests/services/events.json` and `manifests/services/network.json` (node revocation and hook events).@@@ -39,11 +39,11 @@bytes that were scanned, and its scan findings may name the `tool` they are about. `actor.skill@1` and`actor.skill-listing@1` are unchanged.3. **The Link runner** (`[email protected]`, a declared function like `[email protected]`):-- - off unless the owner turns it on (`worker.functions`, `openvibe node skills on`);++ - off unless the owner turns it on (`worker.functions`, `openvibe-link skills on`);- takes `{ tool, entry, input }` and the code as job `inputs` (Media objects pinned by sha256; a mismatch ends thejob before anything runs);- runs `node --permission --allow-fs-read=<the job's input dir>` with no child process, no worker, no addons and-- `--disallow-code-generation-from-strings`, inside the function sandbox Node already has (its own root, limits,++ `--disallow-code-generation-from-strings`, inside the function sandbox Link already has (its own root, limits,no credential, network per `net`);- the input arrives on stdin as JSON; the tool writes one JSON value (≤ 64 KiB; more fails the job `limit`);stderr is kept for the person (≤ 16 KiB).@@@ -74,7 +74,7 @@- **Run the code on Actor's server.** Rejected: Actor holds every person's agent, token and registry record; oneskill's mistake would be a registry-wide incident, and the design rule forbids it outright.- **A container on the host that publishes the skill.** Rejected: the host is someone's machine, and a container-- boundary we do not control is not a sandbox promise we can make to every site. Node already has the sandbox, the++ boundary we do not control is not a sandbox promise we can make to every site. Link already has the sandbox, thelimits, the egress policy and the placement; running there needs no second runtime.- **Python and npm in v1.** Deferred: both widen the runtime surface (packages, install recipes, dependencyresolution) before the scan and the sandbox have a release behind them. Each needs its own review, as does@@@ -82,7 +82,7 @@## Migration and rollback--Both new contracts are additive majors; nothing parses `actor.skill@2` until Actor's registry and Node's runner++Both new contracts are additive majors; nothing parses `actor.skill@2` until Actor's registry and Link's runnership, and @1 keeps working for every existing prompt skill. The only data a sandbox listing adds is the Mediaobjects that hold the code; they are private to Actor's tenant and can be deleted with the listing. Rollback isretiring the @2 listing and turning the runner off: no prompt skill is affected, no job can be submitted without@@@ -103,5 +103,5 @@ the `skill.run` kind, and nothing ever ran on our servers, so there is no server`secret`, `token`, `env` or `key`;- a @2 listing pins each code resource's Media id and sha256, the pinned sha256 is the document's own bytes, and asandbox listing without code is invalid.--Node's own tests (in OpenVibe Link) prove the runner: a tool cannot read env, spawn, write a file, open a socket++Link's own tests (in OpenVibe Link) prove the runner: a tool cannot read env, spawn, write a file, open a socketunder `deny`, or exceed its output.@@@ -42,9 +42,9 @@ Platform-wide decisions from the OpenVibe development roadmap (section 10). Each| [ADR-036](ADR-036-run-execution-authority.md) | Run is an execution authority, not a Host detail: Host deploys persistent applications, Run executes isolated jobs/environments (`function`, `code`, `browser`, `linux`, `desktop`, `gpu`), placed by the Fabric on the cheapest capable offer; provider adapters are offers, not code paths; per-second metering, TTL, idle suspend, cancel, snapshot/resume, stdout streaming, artifacts via Media, limits, network policy and strong isolation | Proposed 2026-10-05 (plan T14; plan §8 lists it to write before its code, and Run's code is owner-blocked), for the owner's review || [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-043](ADR-043-bot-devices-and-control.md) | Bot — devices, pairing, control and safety: robots, devices and credentials are separate (one hashed, rotatable, revocable device credential); pairing is a one-time code; one outbound WebSocket per device with absolute deadlines, device-side stops and a latched e-stop; robot profiles rather than rows; owner/operators/viewers with a timed public queue; everything audited; OpenVibe.Bot is its own service | Accepted 2026-09-29 (plan track T15, "Bot complete"; owner direction the same evening: openvibe.bot is an open control panel for robots, streaming optional; the first robots are the owner's Adeept 4WD Smart Car for Raspberry Pi and a Cozmo-based robot) |--| [ADR-044](ADR-044-actor-runtime.md) | The Actor runtime: one `platform.task@1` task (`POST /v1/tasks`) with a mode, a per-task and per-day budget, result, cost and explanation (a chain of `platform.placement-result@1`); adapters declare capabilities, limits, a reviewed rate card, a trust class and BYO keys; modes are `openvibe-sdk/placement` objectives; live signals auto-balance with hysteresis and immediate failover; a `verifying` state holds cheap-first cross-family checks and the task's own tests; the five ADR-046 trust classes, `private` narrowing to OpenVibe-only, Node control levels underneath; progress over SSE, cancel and webhooks; owner `network` (no Actor service manifest exists yet) | Proposed 2026-10-05 (plan T17), for the owner's review |++| [ADR-044](ADR-044-actor-runtime.md) | The Actor runtime: one `platform.task@1` task (`POST /v1/tasks`) with a mode, a per-task and per-day budget, result, cost and explanation (a chain of `platform.placement-result@1`); adapters declare capabilities, limits, a reviewed rate card, a trust class and BYO keys; modes are `openvibe-sdk/placement` objectives; live signals auto-balance with hysteresis and immediate failover; a `verifying` state holds cheap-first cross-family checks and the task's own tests; the five ADR-046 trust classes, `private` narrowing to OpenVibe-only, Link control levels underneath; progress over SSE, cancel and webhooks; owner `network` (no Actor service manifest exists yet) | Proposed 2026-10-05 (plan T17), for the owner's review || [ADR-045](ADR-045-node-universal-runtime.md) | The Link is OpenVibe's universal runtime, not a Bot/Actor helper: one checksummed release binary and one control link serving the execution worker (`worker:<class>`, per ADR-046 §5), computer control under device-set control levels with a local kill switch and recording, and cache/storage/delivery; local policy always overrides cloud commands; ADR-043 stays the robot/device half | Proposed 2026-10-05 (plan T14 and §8 revision; consistent with ADR-046 and ADR-048, amends ADR-043's credential paragraph), for the owner's review |--| [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 |++| [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>` Link 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 || [ADR-048](ADR-048-services-control-plane.md) | The Services control plane: Services aggregates authorities and owns no other service's rows; every console action is a `common.resource-control-request@1` to the owning authority, answered with `common.resource-control-result@1` (idempotency keys, `confirmation_id` for sensitive actions); OVRN `ovrn:<service>:<project_id>:<type>/<id>` (`common.resource-name@1`) is the one cross-service resource name | Accepted 2026-10-04 (plan T13) || [ADR-053](ADR-053-website-host-boundary.md) | The Website/Host boundary: OpenVibe.Website owns authoring (projects, pages, revisions, builds, Media asset pointers and intended domains) and OpenVibe.Host owns site, deploy, activation, domain binding/verification/TLS and serving; Website keeps no duplicate `host_sites`/`host_deploys` rows and calls Host's routes; a Website build is not a Host deploy; Website composes Host + Codes + Actor + Run + Media and is not OpenVibe.Sites | Proposed 2026-10-05 (plan T19), for the owner's review || [ADR-054](ADR-054-inventory-authority.md) | The OpenVibe inventory: a new authority, OpenVibe.Inventory (`inventory`, `ov_inventory`), owns item kinds (contract + renderer per surface), definitions (`itd_`), instances (`inv_`), the equipped set and an append-only ledger; issuers grant within their namespace; no selling, buying, trading or conversion until a later money ADR (ADR-012/ADR-025); Live's cosmetics are the first kinds, migrated convert-verify-delete | Accepted 2026-10-09 (plan T21) |@@@ -1,7 +1,7 @@# Locker: end-to-end encryption for anything on OpenVibe`locker.envelope@1` (`contracts/locker/envelope.v1.json`) is the format. The library that reads and writes it is--`openvibe-sdk/locker` (Node and the browser); `openvibe-sdk/locker/server` checks an envelope's header without++`openvibe-sdk/locker` (Link and the browser); `openvibe-sdk/locker/server` checks an envelope's header withoutdecrypting it.Locker sits on top of sharing: sharing decides **who may fetch** a thing, Locker decides **who can read** it. With annew file mode 100644@@@ -0,0 +1,7 @@++{++ "provider": "link",++ "machine": "anyone",++ "models": {++ "chat": "llama3.1:8b"++ }++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "provider": "link",++ "machine": "dev_01M5YGM39VC2RRGDQPRD6D4SHD",++ "api_key": "sk-not-for-a-machine"++}nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "provider": "link",++ "models": {++ "chat": "llama3.1:8b"++ }++}ddeleted file mode 100644@@@ -1,7 +0,0 @@--{-- "provider": "node",-- "machine": "anyone",-- "models": {-- "chat": "llama3.1:8b"-- }--}ddeleted file mode 100644@@@ -1,5 +0,0 @@--{-- "provider": "node",-- "machine": "dev_01M5YGM39VC2RRGDQPRD6D4SHD",-- "api_key": "sk-not-for-a-machine"--}ddeleted file mode 100644@@@ -1,6 +0,0 @@--{-- "provider": "node",-- "models": {-- "chat": "llama3.1:8b"-- }--}nnew file mode 100644@@@ -0,0 +1,7 @@++{++ "provider": "link",++ "machine": "any",++ "models": {++ "chat": "llama3.1:8b"++ }++}nnew file mode 100644@@@ -0,0 +1,7 @@++{++ "provider": "link",++ "machine": "dev_01M5YGM39VC2RRGDQPRD6D4SHD",++ "models": {++ "chat": "llama3.1:8b"++ }++}ddeleted file mode 100644@@@ -1,7 +0,0 @@--{-- "provider": "node",-- "machine": "any",-- "models": {-- "chat": "llama3.1:8b"-- }--}ddeleted file mode 100644@@@ -1,7 +0,0 @@--{-- "provider": "node",-- "machine": "dev_01M5YGM39VC2RRGDQPRD6D4SHD",-- "models": {-- "chat": "llama3.1:8b"-- }--}nnew file mode 100644@@@ -0,0 +1,15 @@++{++ "subject": "usr_01M4W5JBFEG3QG9ER8W1A2K0RX",++ "owner": "chat",++ "provider": "link",++ "machine": "dev_01M5YGM39VC2RRGDQPRD6D4SHD",++ "base_url": null,++ "models": {++ "chat": "llama3.1:8b"++ },++ "budget_usd_per_day": null,++ "spent_usd_today": 0,++ "created_at": "2026-10-27T13:00:00.000Z",++ "updated_at": "2026-10-27T13:00:00.000Z",++ "last_used_at": null++}ddeleted file mode 100644@@@ -1,15 +0,0 @@--{-- "subject": "usr_01M4W5JBFEG3QG9ER8W1A2K0RX",-- "owner": "chat",-- "provider": "node",-- "machine": "dev_01M5YGM39VC2RRGDQPRD6D4SHD",-- "base_url": null,-- "models": {-- "chat": "llama3.1:8b"-- },-- "budget_usd_per_day": null,-- "spent_usd_today": 0,-- "created_at": "2026-10-27T13:00:00.000Z",-- "updated_at": "2026-10-27T13:00:00.000Z",-- "last_used_at": null--}@@@ -1,7 +1,7 @@{"id": "mem_01JZ0000000000000000000001","owner": "user:usr_01JZ0000000000000000000AAA",-- "scope": "project:openvibe-node",++ "scope": "project:openvibe-link","kind": "project_rule","text": "Run the Go tests with go test ./... before a commit.","source": "claude-code",@@@ -3,7 +3,7 @@{"text": "Use the repo's formatter before a commit.","kind": "procedure",-- "scope": "project:openvibe-node",++ "scope": "project:openvibe-link","source": "claude-code","source_ref": {"path": ".claude/CLAUDE.md",@@@ -19,6 +19,6 @@"pairing": {"code": "ABCD-EFGH","expires_at": "2026-09-29T19:20:00.000Z",-- "installer": "curl -fsSL https://openvibe.bot/install.sh | sh -s ABCD-EFGH"++ "installer": "curl -fsSL https://openvibe.link/install.sh | sh -s ABCD-EFGH"}}@@@ -9,7 +9,7 @@"type": "user","id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "-ab","status": "online","os": "linux",@@@ -9,7 +9,7 @@"type": "user","id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","status": "online","os": "linux",@@@ -9,7 +9,7 @@"type": "user","id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "ab-","status": "online","os": "linux",@@@ -9,7 +9,7 @@"type": "user","id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "a-------------------------------------------------------------z","status": "online","os": "linux",@@@ -9,7 +9,7 @@"type": "user","id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "a","status": "online","os": "linux",@@@ -9,7 +9,7 @@"type": "user","id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "ab","status": "online","os": "linux",@@@ -9,7 +9,7 @@"type": "user","id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "workstation","status": "online","os": "linux",@@@ -1,12 +1,12 @@{"iss": "https://openvibe.network",-- "sub": "node:nod_42",-- "actor_type": "node",++ "sub": "link:lnk_42",++ "actor_type": "link","aud": ["openvibe.bot"],"cap": [-- "network.node.self.manage"++ "network.link.self.manage"],"iat": 1790000000,"exp": 1790000300,@@@ -1,12 +1,12 @@{"iss": "https://openvibe.network",-- "sub": "node:nod_01JAB2C3D4E5F6G7H8J9K0MNPQ",-- "actor_type": "node",++ "sub": "link:lnk_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "actor_type": "link","aud": ["openvibe.bot"],"cap": [-- "network.node.self.manage"++ "network.link.self.manage"],"iat": 1790000000,"exp": 1790000300,nnew file mode 100644@@@ -0,0 +1,11 @@++{++ "sources": [++ {++ "kind": "invalid",++ "name": "Unknown",++ "status": "ready",++ "reason": null++ }++ ],++ "candidates": []++}nnew file mode 100644@@@ -0,0 +1,42 @@++{++ "sources": [++ {++ "kind": "personal",++ "name": "Your Link machines",++ "status": "ready",++ "reason": null++ },++ {++ "kind": "cloud",++ "name": "OpenVibe cloud machines",++ "status": "coming_soon",++ "reason": "No cloud pool is accepting jobs yet."++ },++ {++ "kind": "exchange",++ "name": "Exchange GPU rentals",++ "status": "coming_soon",++ "reason": "A live quote is required.",++ "price": null++ },++ {++ "kind": "community",++ "name": "Community machines",++ "status": "coming_soon",++ "reason": "Shared placement is being connected."++ }++ ],++ "candidates": [++ {++ "id": "dev_01J0000000000000000000000",++ "kind": "personal",++ "name": "Studio",++ "status": "ready",++ "reason": null,++ "price": {++ "amount_usd": 0,++ "unit": "hour"++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1 @@++{ "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ], "generated_at": "2026-09-28T12:00:05Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ] }nnew file mode 100644@@@ -0,0 +1 @@++{ "source": "openvibe-oregon", "nodes": [] }nnew file mode 100644@@@ -0,0 +1 @@++{ "source": "openvibe-oregon", "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ] }nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "node_id": "node-1",++ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },++ "at": "2026-10-04T18:00:00.000Z"++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",++ "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },++ "at": "2026-10-04T18:00:00.000Z"++}nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",++ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ" },++ "at": "2026-10-04T18:00:00.000Z"++}nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnpt",++ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPT",++ "owner": { "kind": "project", "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ" },++ "at": "2026-10-04T18:00:00.000Z"++}nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",++ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },++ "at": "2026-10-04T18:00:00.000Z"++}nnew file mode 100644@@@ -0,0 +1,7 @@++{++ "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",++ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },++ "reason": "the owner disconnected the machine from Bot",++ "at": "2026-10-04T18:00:00.000Z"++}nnew file mode 100644@@@ -0,0 +1,25 @@++{++ "id": "oregon-1",++ "name": "Oregon 1",++ "roles": [++ "web",++ "app",++ "data",++ "ingest"++ ],++ "location": {++ "region": "us-west",++ "country": "US",++ "city": "Hillsboro",++ "lat": 45.52,++ "lon": -122.99++ },++ "provider": "ovh",++ "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon",++ "health": {++ "status": "up",++ "checked_at": "2026-09-28T12:00:00Z"++ },++ "updated_at": "2026-09-28T12:00:00Z",++ "cell": "WNAM_1"++}nnew file mode 100644@@@ -0,0 +1 @@++{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "usa", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "http://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "address": "15.204.79.215", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": "oregon-1", "name": "Oregon 1", "roles": ["mainframe", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": "probe-fra", "roles": ["edge-probe", "edge-relay"], "location": { "region": "eu-central", "country": "DE" }, "health": { "status": "unknown", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }nnew file mode 100644@@@ -0,0 +1 @@++{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }nnew file mode 100644@@@ -0,0 +1,25 @@++{++ "id": "oregon-1",++ "name": "Oregon 1",++ "roles": [++ "web",++ "app",++ "data",++ "ingest"++ ],++ "location": {++ "region": "us-west",++ "country": "US",++ "city": "Hillsboro",++ "lat": 45.52,++ "lon": -122.99++ },++ "provider": "ovh",++ "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon",++ "health": {++ "status": "up",++ "checked_at": "2026-09-28T12:00:00Z"++ },++ "updated_at": "2026-09-28T12:00:00Z",++ "cell": "wnam-1"++}@@@ -2,5 +2,5 @@"code": "ABCD-1234","pairing_id": "pair_01JAB2C3D4E5F6G7H8J9K0MNPS","expires_at": "2026-10-31T09:00:00.000Z",-- "installer": "curl -fsSL https://openvibe.bot/install | sh -s -- --network https://openvibe.network --pairing pair_01JAB2C3D4E5F6G7H8J9K0MNPS --code ABCD-1234"++ "installer": "curl -fsSL https://openvibe.link/install | sh -s -- --network https://openvibe.network --pairing pair_01JAB2C3D4E5F6G7H8J9K0MNPS --code ABCD-1234"}@@@ -1,6 +1,6 @@{"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",-- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS","owner": "usr_x","at": "2026-10-31T09:00:00.000Z"}@@@ -1,6 +1,6 @@{"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",-- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS","owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS","at": "2026-10-31T09:00:00.000Z"}@@@ -1,6 +1,6 @@{"machine_id": "dev_x",-- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS","owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS","at": "2026-10-31T09:00:00.000Z"}@@@ -1,5 +1,5 @@{"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",-- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS","owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"}@@@ -1,6 +1,6 @@{"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",-- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS","owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS","at": "2026-10-31T09:00:00.000Z"}@@@ -1,6 +1,6 @@{"node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",-- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS","owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS","at": "2026-10-31T09:00:00.000Z"}@@@ -1,6 +1,6 @@{"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",-- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS","owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS","reason": "the owner removed it","at": "2026-10-31T09:00:00.000Z"@@@ -1,6 +1,6 @@{"id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",-- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS","name": "Kitchen server","kind": "server","os": "linux",ddeleted file mode 100644@@@ -1 +0,0 @@--{ "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ] }ddeleted file mode 100644@@@ -1 +0,0 @@--{ "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ], "generated_at": "2026-09-28T12:00:05Z" }ddeleted file mode 100644@@@ -1 +0,0 @@--{ "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ] }ddeleted file mode 100644@@@ -1 +0,0 @@--{ "source": "openvibe-oregon", "nodes": [] }ddeleted file mode 100644@@@ -1 +0,0 @@--{ "source": "openvibe-oregon", "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ] }ddeleted file mode 100644@@@ -1,6 +0,0 @@--{-- "node_id": "node-1",-- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",-- "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },-- "at": "2026-10-04T18:00:00.000Z"--}ddeleted file mode 100644@@@ -1,5 +0,0 @@--{-- "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",-- "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },-- "at": "2026-10-04T18:00:00.000Z"--}ddeleted file mode 100644@@@ -1,6 +0,0 @@--{-- "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",-- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",-- "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ" },-- "at": "2026-10-04T18:00:00.000Z"--}ddeleted file mode 100644@@@ -1,6 +0,0 @@--{-- "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnpt",-- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPT",-- "owner": { "kind": "project", "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ" },-- "at": "2026-10-04T18:00:00.000Z"--}ddeleted file mode 100644@@@ -1,6 +0,0 @@--{-- "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",-- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",-- "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },-- "at": "2026-10-04T18:00:00.000Z"--}ddeleted file mode 100644@@@ -1,7 +0,0 @@--{-- "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",-- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",-- "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },-- "reason": "the owner disconnected the machine from Bot",-- "at": "2026-10-04T18:00:00.000Z"--}ddeleted file mode 100644@@@ -1,25 +0,0 @@--{-- "id": "oregon-1",-- "name": "Oregon 1",-- "roles": [-- "web",-- "app",-- "data",-- "ingest"-- ],-- "location": {-- "region": "us-west",-- "country": "US",-- "city": "Hillsboro",-- "lat": 45.52,-- "lon": -122.99-- },-- "provider": "ovh",-- "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon",-- "health": {-- "status": "up",-- "checked_at": "2026-09-28T12:00:00Z"-- },-- "updated_at": "2026-09-28T12:00:00Z",-- "cell": "WNAM_1"--}ddeleted file mode 100644@@@ -1 +0,0 @@--{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "usa", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }ddeleted file mode 100644@@@ -1 +0,0 @@--{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "http://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }ddeleted file mode 100644@@@ -1 +0,0 @@--{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "address": "15.204.79.215", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }ddeleted file mode 100644@@@ -1 +0,0 @@--{ "id": "oregon-1", "name": "Oregon 1", "roles": ["mainframe", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }ddeleted file mode 100644@@@ -1 +0,0 @@--{ "id": "probe-fra", "roles": ["edge-probe", "edge-relay"], "location": { "region": "eu-central", "country": "DE" }, "health": { "status": "unknown", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }ddeleted file mode 100644@@@ -1 +0,0 @@--{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }ddeleted file mode 100644@@@ -1,25 +0,0 @@--{-- "id": "oregon-1",-- "name": "Oregon 1",-- "roles": [-- "web",-- "app",-- "data",-- "ingest"-- ],-- "location": {-- "region": "us-west",-- "country": "US",-- "city": "Hillsboro",-- "lat": 45.52,-- "lon": -122.99-- },-- "provider": "ovh",-- "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon",-- "health": {-- "status": "up",-- "checked_at": "2026-09-28T12:00:00Z"-- },-- "updated_at": "2026-09-28T12:00:00Z",-- "cell": "wnam-1"--}nnew file mode 100644@@@ -0,0 +1,37 @@++{++ "node_id": "node-1",++ "cpu": {++ "cores": 8,++ "threads": 16++ },++ "arch": "x86_64",++ "memory_mb": 16384,++ "gpu": {++ "model": "L4",++ "count": 1,++ "vram_mb": 24576++ },++ "storage": {++ "capacity_gb": 500,++ "available_gb": 300,++ "kind": "ssd"++ },++ "network": {++ "ingress_mbps": 1000,++ "egress_mbps": 1000++ },++ "regions": [++ "us-west"++ ],++ "tags": [++ "gpu",++ "edge"++ ],++ "costs": {++ "per_hour_usd": 0.2,++ "egress_per_gb_usd": 0.01++ },++ "capabilities": [++ "ffmpeg"++ ]++}nnew file mode 100644@@@ -0,0 +1,33 @@++{++ "cpu": {++ "cores": 8,++ "threads": 16++ },++ "arch": "x86_64",++ "memory_mb": 16384,++ "gpu": {++ "model": "L4",++ "count": 1,++ "vram_mb": 24576++ },++ "storage": {++ "capacity_gb": 500,++ "available_gb": 300,++ "kind": "ssd"++ },++ "network": {++ "ingress_mbps": 1000,++ "egress_mbps": 1000++ },++ "regions": [++ "us-west"++ ],++ "tags": [++ "gpu",++ "edge"++ ],++ "costs": {++ "per_hour_usd": 0.2,++ "egress_per_gb_usd": 0.01++ }++}nnew file mode 100644@@@ -0,0 +1,40 @@++{++ "node_id": "node-1",++ "cpu": {++ "cores": 8,++ "threads": 16++ },++ "arch": "x86_64",++ "memory_mb": 16384,++ "gpu": {++ "model": "L4",++ "count": 1,++ "vram_mb": 24576++ },++ "storage": {++ "capacity_gb": 500,++ "available_gb": 300,++ "kind": "ssd"++ },++ "network": {++ "ingress_mbps": 1000,++ "egress_mbps": 1000++ },++ "regions": [++ "us-west"++ ],++ "tags": [++ "gpu",++ "edge"++ ],++ "costs": {++ "per_hour_usd": 0.2,++ "egress_per_gb_usd": 0.01++ },++ "capabilities": [++ "node:http",++ "worker:ffmpeg"++ ],++ "agent_version": "0.9.0",++ "updated_at": "2026-10-02T00:00:00Z"++}nnew file mode 100644@@@ -0,0 +1,34 @@++{++ "node_id": "node-1",++ "cpu": {++ "cores": 8,++ "threads": 16++ },++ "arch": "x86_64",++ "memory_mb": 16384,++ "gpu": {++ "model": "L4",++ "count": 1,++ "vram_mb": 24576++ },++ "storage": {++ "capacity_gb": 500,++ "available_gb": 300,++ "kind": "ssd"++ },++ "network": {++ "ingress_mbps": 1000,++ "egress_mbps": 1000++ },++ "regions": [++ "us-west"++ ],++ "tags": [++ "gpu",++ "edge"++ ],++ "costs": {++ "per_hour_usd": 0.2,++ "egress_per_gb_usd": 0.01++ }++}ddeleted file mode 100644@@@ -1,37 +0,0 @@--{-- "node_id": "node-1",-- "cpu": {-- "cores": 8,-- "threads": 16-- },-- "arch": "x86_64",-- "memory_mb": 16384,-- "gpu": {-- "model": "L4",-- "count": 1,-- "vram_mb": 24576-- },-- "storage": {-- "capacity_gb": 500,-- "available_gb": 300,-- "kind": "ssd"-- },-- "network": {-- "ingress_mbps": 1000,-- "egress_mbps": 1000-- },-- "regions": [-- "us-west"-- ],-- "tags": [-- "gpu",-- "edge"-- ],-- "costs": {-- "per_hour_usd": 0.2,-- "egress_per_gb_usd": 0.01-- },-- "capabilities": [-- "ffmpeg"-- ]--}ddeleted file mode 100644@@@ -1,33 +0,0 @@--{-- "cpu": {-- "cores": 8,-- "threads": 16-- },-- "arch": "x86_64",-- "memory_mb": 16384,-- "gpu": {-- "model": "L4",-- "count": 1,-- "vram_mb": 24576-- },-- "storage": {-- "capacity_gb": 500,-- "available_gb": 300,-- "kind": "ssd"-- },-- "network": {-- "ingress_mbps": 1000,-- "egress_mbps": 1000-- },-- "regions": [-- "us-west"-- ],-- "tags": [-- "gpu",-- "edge"-- ],-- "costs": {-- "per_hour_usd": 0.2,-- "egress_per_gb_usd": 0.01-- }--}ddeleted file mode 100644@@@ -1,40 +0,0 @@--{-- "node_id": "node-1",-- "cpu": {-- "cores": 8,-- "threads": 16-- },-- "arch": "x86_64",-- "memory_mb": 16384,-- "gpu": {-- "model": "L4",-- "count": 1,-- "vram_mb": 24576-- },-- "storage": {-- "capacity_gb": 500,-- "available_gb": 300,-- "kind": "ssd"-- },-- "network": {-- "ingress_mbps": 1000,-- "egress_mbps": 1000-- },-- "regions": [-- "us-west"-- ],-- "tags": [-- "gpu",-- "edge"-- ],-- "costs": {-- "per_hour_usd": 0.2,-- "egress_per_gb_usd": 0.01-- },-- "capabilities": [-- "node:http",-- "worker:ffmpeg"-- ],-- "agent_version": "0.9.0",-- "updated_at": "2026-10-02T00:00:00Z"--}ddeleted file mode 100644@@@ -1,34 +0,0 @@--{-- "node_id": "node-1",-- "cpu": {-- "cores": 8,-- "threads": 16-- },-- "arch": "x86_64",-- "memory_mb": 16384,-- "gpu": {-- "model": "L4",-- "count": 1,-- "vram_mb": 24576-- },-- "storage": {-- "capacity_gb": 500,-- "available_gb": 300,-- "kind": "ssd"-- },-- "network": {-- "ingress_mbps": 1000,-- "egress_mbps": 1000-- },-- "regions": [-- "us-west"-- ],-- "tags": [-- "gpu",-- "edge"-- ],-- "costs": {-- "per_hour_usd": 0.2,-- "egress_per_gb_usd": 0.01-- }--}@@@ -10,5 +10,5 @@"quantity": 0.35,"unit": "s","at": "2026-10-02T10:00:02.250Z",-- "source": "openvibe-node.worker"++ "source": "openvibe-link.worker"}@@@ -10,5 +10,5 @@"quantity": 1,"unit": "s","at": "2026-10-02T10:00:00.250Z",-- "source": "openvibe-node.worker"++ "source": "openvibe-link.worker"}@@@ -1,6 +1,6 @@{"share_id": "shr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","tunnel_token": "ovt_01J8Z3Q4R5S6T7V8W9X0Y1Z2A9_one_time","transport": "websocket"}@@@ -1,4 +1,4 @@{"share_id": "shr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5"++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5"}@@@ -1,6 +1,6 @@{"share_id": "shr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","tunnel_token": "ovt_01J8Z3Q4R5S6T7V8W9X0Y1Z2A9_one_time","transport": "quic","node_version": "0.42.0",@@@ -2,7 +2,7 @@"name": "-ab","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"mode": "signin","allowed_projects": [@@@ -2,7 +2,7 @@"name": "agents-alice","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"mode": "signin","allowed_projects": [@@@ -2,7 +2,7 @@"name": "agents-alice","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"mode": "signin","allowed_projects": [@@@ -2,7 +2,7 @@"name": "agents-alice","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"mode": "signin","allowed_projects": [@@@ -2,7 +2,7 @@"name": "agents-alice","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"mode": "signin","allowed_projects": [@@@ -2,7 +2,7 @@"name": "agents-alice","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"mode": "signin","allowed_projects": [@@@ -2,7 +2,7 @@"name": "agents-alice","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"mode": "signin","allowed_projects": [@@@ -2,7 +2,7 @@"name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"mode": "signin","allowed_projects": [@@@ -2,7 +2,7 @@"name": "ab-","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"mode": "signin","allowed_projects": [@@@ -2,7 +2,7 @@"name": "agents-alice","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"mode": "signin","allowed_projects": [@@@ -2,7 +2,7 @@"name": "agents-alice","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"id": "pol_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4","mode": "signin"@@@ -2,7 +2,7 @@"name": "a-------------------------------------------------------------z","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"mode": "signin","allowed_projects": [@@@ -2,7 +2,7 @@"name": "a","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"mode": "signin","allowed_projects": [@@@ -2,7 +2,7 @@"name": "ab","protocol": "http","local_target": "127.0.0.1:3000",-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","policy": {"mode": "signin","allowed_projects": [@@@ -5,7 +5,7 @@"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},"project_id": null,-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "-ab","hostname": "-ab.openvibe.website","protocol": "http",@@@ -5,7 +5,7 @@"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},"project_id": null,-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","hostname": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.openvibe.website","protocol": "http",@@@ -5,7 +5,7 @@"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},"project_id": null,-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "ab-","hostname": "ab-.openvibe.website","protocol": "http",@@@ -5,7 +5,7 @@"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},"project_id": null,-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "agents-alice","hostname": "agents-alice.openvibe.website","protocol": "http",@@@ -5,7 +5,7 @@"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},"project_id": null,-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "a-------------------------------------------------------------z","hostname": "a-------------------------------------------------------------z.openvibe.website","protocol": "http",@@@ -5,7 +5,7 @@"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},"project_id": null,-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "a","hostname": "a.openvibe.website","protocol": "http",@@@ -5,7 +5,7 @@"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"},"project_id": null,-- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",++ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5","name": "ab","hostname": "ab.openvibe.website","protocol": "http",@@@ -264,14 +264,14 @@},"sub": {"type": "string",-- "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|node:nod_[0-9A-HJKMNP-TV-Z]{26}|agent:agt_[0-9A-HJKMNP-TV-Z]{26})$"++ "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|link:lnk_[0-9A-HJKMNP-TV-Z]{26}|agent:agt_[0-9A-HJKMNP-TV-Z]{26})$"},"actor_type": {"enum": ["service","app","mod",-- "node",++ "link","agent"]},@@@ -15739,14 +15739,14 @@"enum": ["openai","anthropic",-- "node"++ "link"],"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."},"machine": {"type": "string","pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",-- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."++ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."},"base_url": {"type": "string",@@@ -15804,7 +15804,7 @@"if": {"properties": {"provider": {-- "const": "node"++ "const": "link"}},"required": [@@@ -15857,7 +15857,7 @@"minimax","openrouter","custom",-- "node"++ "link"],"description": "anthropic = Anthropic's Messages API; openai = any OpenAI-compatible endpoint; deepseek, minimax and openrouter = those providers; custom = the endpoint in base_url (a URL that speaks the OpenAI or the Anthropic format, by the route it is used on); node = the person's own machine (no key, no base_url, no cost)."},@@@ -15882,7 +15882,7 @@"machine": {"type": "string","pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",-- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model, or `any` of their own."++ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model, or `any` of their own."},"models": {"type": "object",@@@ -15929,7 +15929,7 @@"if": {"properties": {"provider": {-- "const": "node"++ "const": "link"}},"required": [@@@ -16013,7 +16013,7 @@"enum": ["openai","anthropic",-- "node"++ "link"]},"machine": {@@@ -16022,7 +16022,7 @@"null"],"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",-- "description": "provider node: the machine it runs on, or `any` of the person's own (contracts 0.221.0)"++ "description": "provider link: the machine it runs on, or `any` of the person's own (contracts 0.221.0)"},"base_url": {"type": [@@@ -16155,7 +16155,7 @@"minimax","openrouter","custom",-- "node"++ "link"]},"machine": {@@@ -16164,7 +16164,7 @@"null"],"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",-- "description": "provider node: the machine it runs on, or `any` of the person's own"++ "description": "provider link: the machine it runs on, or `any` of the person's own"},"base_url": {"type": [@@@ -16243,7 +16243,7 @@"properties": {"provider": {"not": {-- "const": "node"++ "const": "link"}}},@@@ -18773,7 +18773,7 @@},"operator": {"type": "string",-- "description": "Operator slug or `owner:<subject_id>` for a private node."++ "description": "Operator slug or `owner:<subject_id>` for a private machine."},"host": {"type": [@@@ -54015,7 +54015,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/watch/watch.v1.json","title": "Watch",-- "description": "watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Node probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.",++ "description": "watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Link probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.","type": "object","additionalProperties": false,"required": [@@@ -72957,15 +72957,15 @@}}},-- "network.node": {++ "network.link": {"version": "1.0.0","owner": "network","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/network/node.v1.json",-- "title": "NetworkNode",-- "description": "network.node@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.",++ "$id": "https://openvibe.network/contracts/network/link.v1.json",++ "title": "NetworkLink",++ "description": "network.link@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.","type": "object","required": ["id",@@@ -73081,15 +73081,15 @@}}},-- "network.node-report-request": {++ "network.link-report-request": {"version": "1.0.0","owner": "network","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/network/node-report-request.v1.json",-- "title": "NetworkNodeReportRequest",-- "description": "network.node-report-request@1: POST /internal/nodes/report on OpenVibe.Network (capability network.node.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.",++ "$id": "https://openvibe.network/contracts/network/link-report-request.v1.json",++ "title": "NetworkLinkReportRequest",++ "description": "network.link-report-request@1: POST /internal/links/report on OpenVibe.Network (capability network.link.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.","type": "object","required": ["source",@@@ -73106,21 +73106,21 @@"type": "array","maxItems": 500,"items": {-- "$ref": "node.v1.json"++ "$ref": "link.v1.json"}}}}},-- "network.node-list-result": {++ "network.link-list-result": {"version": "1.0.0","owner": "network","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/network/node-list-result.v1.json",-- "title": "NetworkNodeListResult",-- "description": "network.node-list-result@1: GET /api/v1/nodes on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().",++ "$id": "https://openvibe.network/contracts/network/link-list-result.v1.json",++ "title": "NetworkLinkListResult",++ "description": "network.link-list-result@1: GET /api/v1/links on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().","type": "object","required": ["nodes",@@@ -73131,7 +73131,7 @@"nodes": {"type": "array","items": {-- "$ref": "node.v1.json"++ "$ref": "link.v1.json"}},"generated_at": {@@@ -73167,8 +73167,8 @@},"principal_id": {"type": "string",-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "The machine's node principal on Network (nod_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for."++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The machine's node principal on Network (lnk_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for."},"name": {"type": "string",@@@ -73457,7 +73457,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/network/machine-pairing.v1.json","title": "NetworkMachinePairing",-- "description": "network.machine-pairing@1: a one-time code that pairs a machine with the person's account (POST /api/v1/machines/pairings, capability machines.manage). The code is shown once, expires, and works once: the machine redeems it and becomes the person's. The command is what the owner runs on the machine (openvibe.bot/install). The code is never stored in the clear and never returned again.",++ "description": "network.machine-pairing@1: a one-time code that pairs a machine with the person's account (POST /api/v1/machines/pairings, capability machines.manage). The code is shown once, expires, and works once: the machine redeems it and becomes the person's. The command is what the owner runs on the machine (openvibe.link/install). The code is never stored in the clear and never returned again.","type": "object","required": ["code",@@@ -73484,7 +73484,7 @@"installer": {"type": "string","maxLength": 500,-- "description": "The command the owner runs on the machine (openvibe.bot/install, with the network, the pairing id and the code)."++ "description": "The command the owner runs on the machine (openvibe.link/install, with the network, the pairing id and the code)."}}}@@@ -73512,7 +73512,7 @@},"principal_id": {"type": "string",-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$","description": "The machine's node principal on Network, when it has one."},"owner": {@@@ -73550,7 +73550,7 @@},"principal_id": {"type": "string",-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$","description": "The machine's node principal on Network, when it has one."},"owner": {@@@ -73573,7 +73573,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/events/payloads/network.machine.paired.v1.json","title": "NetworkMachinePairedPayload",-- "description": "network.machine.paired v1 (OpenVibe.Network, plan L34: the machines hub). A machine was paired with a person's account: it redeemed the one-time code and became theirs. It holds a credential from then on; the code is spent. The machine's device id in Bot is absent until the machine checks in for the first time. Envelope: subject { type: machine, id: <nod_…> }, visibility internal, actor service:network.",++ "description": "network.machine.paired v1 (OpenVibe.Network, plan L34: the machines hub). A machine was paired with a person's account: it redeemed the one-time code and became theirs. It holds a credential from then on; the code is spent. The machine's device id in Bot is absent until the machine checks in for the first time. Envelope: subject { type: machine, id: <lnk_…> }, visibility internal, actor service:network.","type": "object","required": ["principal_id",@@@ -73594,7 +73594,7 @@},"principal_id": {"type": "string",-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$","description": "The node principal the pairing created. Also the envelope subject id."},"owner": {@@@ -73680,7 +73680,7 @@},"principal_id": {"type": "string",-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$","description": "The machine's node principal on Network, when it had one."},"owner": {@@@ -73879,7 +73879,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/platform/resource-offer.v1.json","title": "ResourceOffer",-- "description": "What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.node@1) deliberately leaves out.",++ "description": "What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.link@1) deliberately leaves out.","type": "object","additionalProperties": false,"required": [@@@ -73910,7 +73910,7 @@},"node_id": {"type": "string",-- "description": "network.node@1 id, for kind node"++ "description": "network.link@1 id, for kind node"},"provider": {"type": "string",@@@ -73948,7 +73948,7 @@"type": "string","pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"},-- "description": "e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2. 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 Link'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. Beside them, a kind harness names `runtime:<class>` for each platform.runtime-class@1 class it can run (runtime:function, runtime:code, runtime:browser, runtime:linux, runtime:desktop, runtime:gpu); these are ordinary capability names, not reserved, and OpenVibe.Codes already advertises the classes it offers."++ "description": "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 Link's job worker, one per class: worker:function, worker:code, worker:browser, worker:linux, worker:desktop and worker:gpu ($defs.reservedWorkerCapabilities). A Link advertises the name of each class it runs (Link's status.capabilities.worker); every other `worker:` name (worker:ffmpeg, worker:ai-gpu) is an ordinary capability name. Beside them, a kind harness names `runtime:<class>` for each platform.runtime-class@1 class it can run (runtime:function, runtime:code, runtime:browser, runtime:linux, runtime:desktop, runtime:gpu); these are ordinary capability names, not reserved, and OpenVibe.Codes already advertises the classes it offers."},"capacity": {"type": "object",@@@ -75221,7 +75221,7 @@"quantity": 1,"unit": "s","at": "2026-10-02T10:00:00.250Z",-- "source": "openvibe-node.worker"++ "source": "openvibe-link.worker"}]}@@@ -75357,15 +75357,222 @@]}},-- "platform.node-capabilities": {++ "link.compute": {"version": "1.0.0","owner": "network","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/platform/node-capabilities.v1.json",-- "title": "NodeCapabilities",-- "description": "Node hardware, placement regions, tags, and operating costs (T1 Universal Fabric; ADR-034 proposed).",++ "$id": "https://openvibe.network/contracts/link/compute.v1.json",++ "title": "LinkCompute",++ "description": "Where a person's work can run. Only ready candidates may be picked; a missing rental quote is never a price.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "sources",++ "candidates"++ ],++ "properties": {++ "sources": {++ "type": "array",++ "items": {++ "$ref": "#/$defs/source"++ }++ },++ "candidates": {++ "type": "array",++ "items": {++ "$ref": "#/$defs/candidate"++ }++ }++ },++ "$defs": {++ "kind": {++ "enum": [++ "personal",++ "cloud",++ "exchange",++ "community"++ ]++ },++ "status": {++ "enum": [++ "ready",++ "unavailable",++ "coming_soon"++ ]++ },++ "price": {++ "type": [++ "object",++ "null"++ ],++ "additionalProperties": false,++ "required": [++ "amount_usd",++ "unit"++ ],++ "properties": {++ "amount_usd": {++ "type": "number",++ "minimum": 0++ },++ "unit": {++ "enum": [++ "hour"++ ]++ }++ }++ },++ "source": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "kind",++ "name",++ "status",++ "reason"++ ],++ "properties": {++ "id": {++ "type": "string",++ "minLength": 1++ },++ "principal_id": {++ "type": "string",++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "kind": {++ "$ref": "#/$defs/kind"++ },++ "name": {++ "type": "string",++ "minLength": 1++ },++ "status": {++ "$ref": "#/$defs/status"++ },++ "reason": {++ "type": [++ "string",++ "null"++ ]++ },++ "price": {++ "$ref": "#/$defs/price"++ },++ "quoted_at": {++ "type": "string",++ "format": "date-time"++ },++ "expires_at": {++ "type": "string",++ "format": "date-time"++ }++ },++ "if": {++ "properties": {++ "kind": {++ "const": "exchange"++ },++ "status": {++ "const": "ready"++ }++ },++ "required": [++ "kind",++ "status"++ ]++ },++ "then": {++ "required": [++ "price",++ "quoted_at",++ "expires_at"++ ],++ "properties": {++ "price": {++ "type": "object"++ }++ }++ }++ },++ "candidate": {++ "allOf": [++ {++ "$ref": "#/$defs/source"++ },++ {++ "type": "object",++ "required": [++ "id",++ "price"++ ],++ "properties": {++ "id": {++ "type": "string",++ "minLength": 1++ }++ }++ }++ ]++ }++ },++ "examples": [++ {++ "sources": [++ {++ "kind": "personal",++ "name": "Your Link machines",++ "status": "ready",++ "reason": null++ },++ {++ "kind": "cloud",++ "name": "OpenVibe cloud machines",++ "status": "coming_soon",++ "reason": "No cloud pool is accepting jobs yet."++ },++ {++ "kind": "exchange",++ "name": "Exchange GPU rentals",++ "status": "coming_soon",++ "reason": "A live quote is required.",++ "price": null++ },++ {++ "kind": "community",++ "name": "Community machines",++ "status": "coming_soon",++ "reason": "Shared placement is being connected."++ }++ ],++ "candidates": [++ {++ "id": "dev_01J0000000000000000000000",++ "kind": "personal",++ "name": "Studio",++ "status": "ready",++ "reason": null,++ "price": {++ "amount_usd": 0,++ "unit": "hour"++ }++ }++ ]++ }++ ]++ }++ },++ "platform.link-capabilities": {++ "version": "1.0.0",++ "owner": "network",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/platform/link-capabilities.v1.json",++ "title": "LinkCapabilities",++ "description": "Link machine hardware, placement regions, tags, and operating costs (T1 Universal Fabric; ADR-034 proposed).","$comment": "Additive optional fields may extend this v1 contract.","type": "object","additionalProperties": false,@@@ -76923,7 +77130,7 @@{"type": "object","additionalProperties": false,-- "description": "Device to server: wall-clock second `second` of the job has fully elapsed. Metering (platform.usage-sample@1, written through billing.usage.record by whoever terminates the link: OpenVibe.Bot now, Fabric later): second n is [started_ms + n*1000, started_ms + (n+1)*1000) on the worker's monotonic clock. A worker sends `job_usage` for n only once that second has fully elapsed, so every `job_usage` stands for quantity 1; the partial last second is never sent here, only in `job_exit`. Each second is exactly one reading: `id` and `idempotency_key` `run:<job id>:<n>`, `service` `run`, `operation` `function.invoke`, `unit` `s`, `quantity` 1 (the partial last second: (wall_ms mod 1000)/1000, never 0), `at` new Date(started_ms + n*1000).toISOString(), `resource` the job id, `node` the worker's device id, `source` `openvibe-node.worker`, and `project`, `subject` and `provider` from the dispatcher's own record of the job. No field depends on when or how often a frame was sent, and no rating fields (`cost_estimate`, `free_allowance_used`, `vibes_charged`) are set, because Billing keeps the first reading under a key, replays an identical one and refuses a different one (409): a resend after a reconnect, a `job_usage` and the `job_exit` backfill of the same second, and two writers of one job all land on one record.",++ "description": "Device to server: wall-clock second `second` of the job has fully elapsed. Metering (platform.usage-sample@1, written through billing.usage.record by whoever terminates the link: OpenVibe.Bot now, Fabric later): second n is [started_ms + n*1000, started_ms + (n+1)*1000) on the worker's monotonic clock. A worker sends `job_usage` for n only once that second has fully elapsed, so every `job_usage` stands for quantity 1; the partial last second is never sent here, only in `job_exit`. Each second is exactly one reading: `id` and `idempotency_key` `run:<job id>:<n>`, `service` `run`, `operation` `function.invoke`, `unit` `s`, `quantity` 1 (the partial last second: (wall_ms mod 1000)/1000, never 0), `at` new Date(started_ms + n*1000).toISOString(), `resource` the job id, `node` the worker's device id, `source` `openvibe-link.worker`, and `project`, `subject` and `provider` from the dispatcher's own record of the job. No field depends on when or how often a frame was sent, and no rating fields (`cost_estimate`, `free_allowance_used`, `vibes_charged`) are set, because Billing keeps the first reading under a key, replays an identical one and refuses a different one (409): a resend after a reconnect, a `job_usage` and the `job_exit` backfill of the same second, and two writers of one job all land on one record.","required": ["v","seq",@@@ -82245,7 +82452,7 @@"null"],"maxLength": 128,-- "description": "The offer's provider, as Run sent it; null for a first-party node."++ "description": "The offer's provider, as Run sent it; null for a first-party machine."},"cancel_requested": {"type": "boolean",@@@ -83789,7 +83996,7 @@"null"],"maxLength": 128,-- "description": "The offer's provider (the usage reading's `provider`); null for a first-party node."++ "description": "The offer's provider (the usage reading's `provider`); null for a first-party machine."},"region": {"type": [@@@ -85328,15 +85535,15 @@}}},-- "network.node.revoked": {++ "network.link.revoked": {"version": "1.0.0","owner": "network","status": "active","schema": {"$schema": "https://json-schema.org/draft/2020-12/schema",-- "$id": "https://openvibe.network/contracts/events/payloads/network.node.revoked.v1.json",-- "title": "NetworkNodeRevokedPayload",-- "description": "network.node.revoked v1 (OpenVibe.Network; plan T2, docs/t2-cells-and-node-principal.md section 10, migrations/0014_node_pairing.sql). A node principal (nod_…), a person's paired machine, was revoked: through POST /internal/node-principals/:id/revoke (network.node.manage, the service that paired it), through POST /api/v1/me/nodes/:id/revoke (the owner's session), or with the owner's account. Network revokes the row and clears the credential and its grace window; a machine holding a live node token is refused at its next use (≤ 300 s), and until this event only learned of the revoke at its next reauth (≤ 330 s). A consumer stops the machine at once; a second revoke changes nothing and emits nothing. The credential and its hash are never carried. Envelope: subject { type: node_principal, id: <nod_…> }, visibility internal, actor the service (svc:<service>) or the owner ({ type: user }) that revoked.",++ "$id": "https://openvibe.network/contracts/events/payloads/network.link.revoked.v1.json",++ "title": "NetworkLinkRevokedPayload",++ "description": "network.link.revoked v1 (OpenVibe.Network; plan T2, docs/t2-cells-and-node-principal.md section 10, migrations/0014_node_pairing.sql). A node principal (lnk_…), a person's paired machine, was revoked: through POST /internal/link-principals/:id/revoke (network.link.manage, the service that paired it), through POST /api/v1/me/nodes/:id/revoke (the owner's session), or with the owner's account. Network revokes the row and clears the credential and its grace window; a machine holding a live node token is refused at its next use (≤ 300 s), and until this event only learned of the revoke at its next reauth (≤ 330 s). A consumer stops the machine at once; a second revoke changes nothing and emits nothing. The credential and its hash are never carried. Envelope: subject { type: node_principal, id: <lnk_…> }, visibility internal, actor the service (svc:<service>) or the owner ({ type: user }) that revoked.","type": "object","required": ["node_id",@@@ -85353,7 +85560,7 @@},"principal_id": {"type": "string",-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$","description": "The revoked node principal. Also the envelope subject id."},"owner": {@@@ -103862,7 +104069,7 @@"node","submitted"],-- "description": "codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it."++ "description": "codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it."},"id": {"type": "string",@@@ -104111,7 +104318,7 @@"node","submitted"],-- "description": "codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it."++ "description": "codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it."},"id": {"type": "string",@@@ -105252,7 +105459,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/website/share.v1.json","title": "WebsiteShare",-- "description": "website.share@1: one Share on OpenVibe.Website Connect, a local port published at a stable hostname through the edge relay. The record says who owns it, which project scopes it, which Node tunnel serves it, which local target it may reach, which policy version the edge enforces and where it is in its life (draft, review, active, paused, revoked). The tunnel token, the request bodies the inspector captures and the policy's password hash are never part of this document.",++ "description": "website.share@1: one Share on OpenVibe.Website Connect, a local port published at a stable hostname through the edge relay. The record says who owns it, which project scopes it, which Link tunnel serves it, which local target it may reach, which policy version the edge enforces and where it is in its life (draft, review, active, paused, revoked). The tunnel token, the request bodies the inspector captures and the policy's password hash are never part of this document.","type": "object","additionalProperties": false,"required": [@@@ -105294,7 +105501,7 @@"string","null"],-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$","description": "The Network node principal expected to serve the tunnel; null until one is bound."},"name": {@@@ -105372,7 +105579,7 @@"null"],"format": "date-time",-- "description": "The last successful Node liveness check; null until the first tunnel."++ "description": "The last successful Link liveness check; null until the first tunnel."},"created_at": {"type": "string",@@@ -105455,8 +105662,8 @@},"node_id": {"type": "string",-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "Optional: bind the share to this Node principal now. Omit to bind it when the first tunnel connects."++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "Optional: bind the share to this Link principal now. Omit to bind it when the first tunnel connects."},"region": {"type": "string",@@@ -105489,7 +105696,7 @@},"node_id": {"type": "string",-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$","description": "Must equal the node principal in the Network token; a mismatch is refused and audited."},"tunnel_token": {@@@ -105995,7 +106202,7 @@"string","null"],-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$","description": "The Network node principal the device presents; null for an agent or server that joins through a pre-auth key."},"name": {@@@ -111902,7 +112109,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/bot/machine-pool.v1.json","title": "BotMachinePool",-- "description": "bot.machine-pool@1 (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC, `rpc-server` on the members and `llama-server --rpc` on the head. OpenVibe.Network holds the record: the owner proposes it on /machines (POST /api/v1/machine-pools, two to eight of their own machines and an optional head; the default head has the most free VRAM), and each machine reads it with its own node token (GET /api/v1/node/self/pools/:id, network.node.self.manage) and approves it locally (POST /api/v1/node/self/pools/:id/approve with { certificate }). A machine's `certificate` is null until it approves on that machine: the Link generates an Ed25519 key there, keeps the private key, and uploads only its self-signed public certificate, which the other members pin for the pool's private link. A pool with any null certificate never serves. The record never carries a private key or a prompt. What a schema cannot say, Network and Node check at run time: the members are distinct and the head is one of them; every member has the pool's owner, today, through Bot; every member was seen from the same public address with a private address in the same subnet; the head's direct link probe to each member passes; each machine's certificate is its own; and any change of membership, owner, head or key revokes the pool, so all machines approve a new one. A machine that leaves (removed, signed out, a different owner) drops its link and its key at once.",++ "description": "bot.machine-pool@1 (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC, `rpc-server` on the members and `llama-server --rpc` on the head. OpenVibe.Network holds the record: the owner proposes it on /machines (POST /api/v1/machine-pools, two to eight of their own machines and an optional head; the default head has the most free VRAM), and each machine reads it with its own Link token (GET /api/v1/link/self/pools/:id, network.link.self.manage) and approves it locally (POST /api/v1/link/self/pools/:id/approve with { certificate }). A machine's `certificate` is null until it approves on that machine: the Link generates an Ed25519 key there, keeps the private key, and uploads only its self-signed public certificate, which the other members pin for the pool's private link. A pool with any null certificate never serves. The record never carries a private key or a prompt. What a schema cannot say, Network and Link check at run time: the members are distinct and the head is one of them; every member has the pool's owner, today, through Bot; every member was seen from the same public address with a private address in the same subnet; the head's direct link probe to each member passes; each machine's certificate is its own; and any change of membership, owner, head or key revokes the pool, so all machines approve a new one. A machine that leaves (removed, signed out, a different owner) drops its link and its key at once.","type": "object","additionalProperties": false,"required": [@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Actor API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.AI API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"@@@ -773,7 +773,7 @@"enum": ["openai","anthropic",-- "node"++ "link"],"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."},@@@ -786,9 +786,9 @@"schema": {"type": "string","pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",-- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."++ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."},-- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."++ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."},{"name": "base_url",@@@ -928,7 +928,7 @@"enum": ["openai","anthropic",-- "node"++ "link"],"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."},@@@ -941,9 +941,9 @@"schema": {"type": "string","pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",-- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."++ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."},-- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."++ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."},{"name": "base_url",@@@ -5296,14 +5296,14 @@"enum": ["openai","anthropic",-- "node"++ "link"],"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."},"machine": {"type": "string","pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",-- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."++ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."},"base_url": {"type": "string",@@@ -5361,7 +5361,7 @@"if": {"properties": {"provider": {-- "const": "node"++ "const": "link"}},"required": [@@@ -5416,7 +5416,7 @@"enum": ["openai","anthropic",-- "node"++ "link"]},"machine": {@@@ -5425,7 +5425,7 @@"null"],"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",-- "description": "provider node: the machine it runs on, or `any` of the person's own (contracts 0.221.0)"++ "description": "provider link: the machine it runs on, or `any` of the person's own (contracts 0.221.0)"},"base_url": {"type": [@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bench API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"@@@ -2462,7 +2462,7 @@"node","submitted"],-- "description": "codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it."++ "description": "codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it."},"id": {"type": "string",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Billing API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"@@@ -6456,7 +6456,7 @@"quantity": 1,"unit": "s","at": "2026-10-02T10:00:00.250Z",-- "source": "openvibe-node.worker"++ "source": "openvibe-link.worker"}]}@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Blog API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"@@@ -476,7 +476,7 @@"get": {"operationId": "get_api_v1_machines","summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",-- "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.network/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",++ "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.link/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.","tags": ["bot.machine"],@@@ -525,7 +525,7 @@"get": {"operationId": "get_api_v1_machines_id","summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",-- "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.network/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",++ "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.link/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.","tags": ["bot.machine"],@@@ -582,7 +582,7 @@"patch": {"operationId": "patch_api_v1_machines_id","summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",-- "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.network/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",++ "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.link/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.","tags": ["bot.machine"],@@@ -992,7 +992,7 @@"post": {"operationId": "post_api_v1_machines_id_revoke","summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",-- "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.network/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",++ "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.link/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.","tags": ["bot.machine"],@@@ -1058,7 +1058,7 @@"put": {"operationId": "put_api_v1_machines_id_roles","summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",-- "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.network/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",++ "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.link/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.","tags": ["bot.machine"],@@@ -2563,7 +2563,7 @@"null"],"maxLength": 128,-- "description": "The offer's provider, as Run sent it; null for a first-party node."++ "description": "The offer's provider, as Run sent it; null for a first-party machine."},"cancel_requested": {"type": "boolean",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Chat API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.372.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 Connect API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Design API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Domains API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Exchange API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Exchange as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Exchange as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Food API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Inventory API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Mail API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.MediaHub API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"@@@ -192,38 +192,70 @@}}},-- "/api/v1/machines": {-- "get": {-- "operationId": "get_api_v1_machines",-- "summary": "Read a person's own machines: the list, one machine, and the catalogue of roles.",-- "description": "**network.machines.read** (public): Read a person's own machines: the list, one machine, and the catalogue of roles. A person reads their own with their own token; an app needs the person's consent for this scope (it is a public capability, so it can be granted to an app, and the app then acts only on behalf of the person who consented).",++ "/api/v1/link/self/capabilities": {++ "put": {++ "operationId": "put_api_v1_link_self_capabilities",++ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",++ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.","tags": [-- "network.machines"++ "network.link"],"x-openvibe-capabilities": [-- "network.machines.read"++ "network.link.self.manage"],"x-openvibe-visibility": [-- "public"++ "internal"],-- "x-openvibe-quota-class": "network-read",++ "x-openvibe-quota-class": "admin","security": [{"openvibeToken": [-- "network.machines.read"++ "network.link.self.manage"]}],"responses": {"2XX": {-- "description": "Success",++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)","content": {-- "application/json": {++ "application/problem+json": {"schema": {-- "$ref": "#/components/schemas/network.machine-list-result.v1"++ "$ref": "#/components/schemas/errors.problem.v1"}}}++ }++ }++ }++ },++ "/api/v1/link/self/credential": {++ "post": {++ "operationId": "post_api_v1_link_self_credential",++ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",++ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",++ "tags": [++ "network.link"++ ],++ "x-openvibe-capabilities": [++ "network.link.self.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "admin",++ "security": [++ {++ "openvibeToken": [++ "network.link.self.manage"++ ]++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"},"default": {"description": "An error (RFC 9457 problem details)",@@@ -238,25 +270,25 @@}}},-- "/api/v1/machines/{id}": {++ "/api/v1/link/self/pools/{id}": {"get": {-- "operationId": "get_api_v1_machines_id",-- "summary": "Read a person's own machines: the list, one machine, and the catalogue of roles.",-- "description": "**network.machines.read** (public): Read a person's own machines: the list, one machine, and the catalogue of roles. A person reads their own with their own token; an app needs the person's consent for this scope (it is a public capability, so it can be granted to an app, and the app then acts only on behalf of the person who consented).",++ "operationId": "get_api_v1_link_self_pools_id",++ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",++ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.","tags": [-- "network.machines"++ "network.link"],"x-openvibe-capabilities": [-- "network.machines.read"++ "network.link.self.manage"],"x-openvibe-visibility": [-- "public"++ "internal"],-- "x-openvibe-quota-class": "network-read",++ "x-openvibe-quota-class": "admin","security": [{"openvibeToken": [-- "network.machines.read"++ "network.link.self.manage"]}],@@@ -272,14 +304,7 @@],"responses": {"2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/network.machine-list-result.v1"-- }-- }-- }++ "description": "Success"},"default": {"description": "An error (RFC 9457 problem details)",@@@ -292,38 +317,30 @@}}}-- },-- "patch": {-- "operationId": "patch_api_v1_machines_id",-- "summary": "Pair, rename, give roles to and revoke a person's own machines.",-- "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.",++ }++ },++ "/api/v1/link/self/pools/{id}/approve": {++ "post": {++ "operationId": "post_api_v1_link_self_pools_id_approve",++ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",++ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.","tags": [-- "network.machines"++ "network.link"],"x-openvibe-capabilities": [-- "network.machines.manage"++ "network.link.self.manage"],"x-openvibe-visibility": [-- "public"++ "internal"],-- "x-openvibe-quota-class": "network-write",++ "x-openvibe-quota-class": "admin","security": [{"openvibeToken": [-- "network.machines.manage"++ "network.link.self.manage"]}],-- "requestBody": {-- "required": false,-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/bot.machine-manage-request.v1"-- }-- }-- }-- },"parameters": [{"name": "id",@@@ -336,14 +353,7 @@],"responses": {"2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/network.machine-manage-result.v1"-- }-- }-- }++ "description": "Success"},"default": {"description": "An error (RFC 9457 problem details)",@@@ -358,38 +368,28 @@}}},-- "/api/v1/machines/{id}/revoke": {++ "/api/v1/link/self/pools/{id}/leave": {"post": {-- "operationId": "post_api_v1_machines_id_revoke",-- "summary": "Pair, rename, give roles to and revoke a person's own machines.",-- "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.",++ "operationId": "post_api_v1_link_self_pools_id_leave",++ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",++ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.","tags": [-- "network.machines"++ "network.link"],"x-openvibe-capabilities": [-- "network.machines.manage"++ "network.link.self.manage"],"x-openvibe-visibility": [-- "public"++ "internal"],-- "x-openvibe-quota-class": "network-write",++ "x-openvibe-quota-class": "admin","security": [{"openvibeToken": [-- "network.machines.manage"++ "network.link.self.manage"]}],-- "requestBody": {-- "required": false,-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/bot.machine-manage-request.v1"-- }-- }-- }-- },"parameters": [{"name": "id",@@@ -402,14 +402,7 @@],"responses": {"2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/network.machine-manage-result.v1"-- }-- }-- }++ "description": "Success"},"default": {"description": "An error (RFC 9457 problem details)",@@@ -424,38 +417,28 @@}}},-- "/api/v1/machines/{id}/roles": {-- "put": {-- "operationId": "put_api_v1_machines_id_roles",-- "summary": "Pair, rename, give roles to and revoke a person's own machines.",-- "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.",++ "/api/v1/link/self/pools/{id}/status": {++ "post": {++ "operationId": "post_api_v1_link_self_pools_id_status",++ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",++ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.","tags": [-- "network.machines"++ "network.link"],"x-openvibe-capabilities": [-- "network.machines.manage"++ "network.link.self.manage"],"x-openvibe-visibility": [-- "public"++ "internal"],-- "x-openvibe-quota-class": "network-write",++ "x-openvibe-quota-class": "admin","security": [{"openvibeToken": [-- "network.machines.manage"++ "network.link.self.manage"]}],-- "requestBody": {-- "required": false,-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/bot.machine-manage-request.v1"-- }-- }-- }-- },"parameters": [{"name": "id",@@@ -468,14 +451,7 @@],"responses": {"2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/network.machine-manage-result.v1"-- }-- }-- }++ "description": "Success"},"default": {"description": "An error (RFC 9457 problem details)",@@@ -490,48 +466,31 @@}}},-- "/api/v1/machines/pairings": {++ "/api/v1/link/self/pools/announce": {"post": {-- "operationId": "post_api_v1_machines_pairings",-- "summary": "Pair, rename, give roles to and revoke a person's own machines.",-- "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.",++ "operationId": "post_api_v1_link_self_pools_announce",++ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",++ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.","tags": [-- "network.machines"++ "network.link"],"x-openvibe-capabilities": [-- "network.machines.manage"++ "network.link.self.manage"],"x-openvibe-visibility": [-- "public"++ "internal"],-- "x-openvibe-quota-class": "network-write",++ "x-openvibe-quota-class": "admin","security": [{"openvibeToken": [-- "network.machines.manage"++ "network.link.self.manage"]}],-- "requestBody": {-- "required": false,-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/bot.machine-manage-request.v1"-- }-- }-- }-- },"responses": {"2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/network.machine-manage-result.v1"-- }-- }-- }++ "description": "Success"},"default": {"description": "An error (RFC 9457 problem details)",@@@ -546,9 +505,9 @@}}},-- "/api/v1/machines/roles": {++ "/api/v1/machines": {"get": {-- "operationId": "get_api_v1_machines_roles",++ "operationId": "get_api_v1_machines","summary": "Read a person's own machines: the list, one machine, and the catalogue of roles.","description": "**network.machines.read** (public): Read a person's own machines: the list, one machine, and the catalogue of roles. A person reads their own with their own token; an app needs the person's consent for this scope (it is a public capability, so it can be granted to an app, and the app then acts only on behalf of the person who consented).","tags": [@@@ -592,35 +551,45 @@}}},-- "/api/v1/me/actions": {++ "/api/v1/machines/{id}": {"get": {-- "operationId": "get_api_v1_me_actions",-- "summary": "The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and f",-- "description": "**network.actions.read** (public): The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and failed (network.me-actions-result@1); POST /api/v1/me/actions/:service/:id/:action approves, declines, cancels or retries through the owning authority. Readable from every OpenVibe site with the person's Network token, like the bell.",++ "operationId": "get_api_v1_machines_id",++ "summary": "Read a person's own machines: the list, one machine, and the catalogue of roles.",++ "description": "**network.machines.read** (public): Read a person's own machines: the list, one machine, and the catalogue of roles. A person reads their own with their own token; an app needs the person's consent for this scope (it is a public capability, so it can be granted to an app, and the app then acts only on behalf of the person who consented).","tags": [-- "network.actions"++ "network.machines"],"x-openvibe-capabilities": [-- "network.actions.read"++ "network.machines.read"],"x-openvibe-visibility": ["public"],-- "x-openvibe-quota-class": "standard",++ "x-openvibe-quota-class": "network-read","security": [{"openvibeToken": [-- "network.actions.read"++ "network.machines.read"]}],++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],"responses": {"2XX": {"description": "Success","content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/network.me-actions-result.v1"++ "$ref": "#/components/schemas/network.machine-list-result.v1"}}}@@@ -636,39 +605,39 @@}}}-- }-- },-- "/api/v1/me/actions/{service}/{id}/{action}": {-- "post": {-- "operationId": "post_api_v1_me_actions_service_id_action",-- "summary": "The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and f",-- "description": "**network.actions.read** (public): The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and failed (network.me-actions-result@1); POST /api/v1/me/actions/:service/:id/:action approves, declines, cancels or retries through the owning authority. Readable from every OpenVibe site with the person's Network token, like the bell.",++ },++ "patch": {++ "operationId": "patch_api_v1_machines_id",++ "summary": "Pair, rename, give roles to and revoke a person's own machines.",++ "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.","tags": [-- "network.actions"++ "network.machines"],"x-openvibe-capabilities": [-- "network.actions.read"++ "network.machines.manage"],"x-openvibe-visibility": ["public"],-- "x-openvibe-quota-class": "standard",++ "x-openvibe-quota-class": "network-write","security": [{"openvibeToken": [-- "network.actions.read"++ "network.machines.manage"]}],-- "parameters": [-- {-- "name": "service",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/bot.machine-manage-request.v1"++ }}-- },++ }++ },++ "parameters": [{"name": "id","in": "path",@@@ -676,14 +645,6 @@"schema": {"type": "string"}-- },-- {-- "name": "action",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }}],"responses": {@@@ -692,7 +653,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/network.me-actions-result.v1"++ "$ref": "#/components/schemas/network.machine-manage-result.v1"}}}@@@ -710,25 +671,25 @@}}},-- "/api/v1/me/actions/skills/{id}/run": {++ "/api/v1/machines/{id}/revoke": {"post": {-- "operationId": "post_api_v1_me_actions_skills_id_run",-- "summary": "Start one of Actor's catalogue skills as the signed-in person (plan 2026-11-09 §7): POST /api/v1/me/actions/skills/:id/run asks Actor to run that skill for them, with the input and the choice.",-- "description": "**network.skill.run** (public): Start one of Actor's catalogue skills as the signed-in person (plan 2026-11-09 §7): POST /api/v1/me/actions/skills/:id/run asks Actor to run that skill for them, with the input and the choice. Network never runs the skill itself; Actor does, under the person's own approvals and budget.",++ "operationId": "post_api_v1_machines_id_revoke",++ "summary": "Pair, rename, give roles to and revoke a person's own machines.",++ "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.","tags": [-- "network.skill"++ "network.machines"],"x-openvibe-capabilities": [-- "network.skill.run"++ "network.machines.manage"],"x-openvibe-visibility": ["public"],-- "x-openvibe-quota-class": "standard",++ "x-openvibe-quota-class": "network-write","security": [{"openvibeToken": [-- "network.skill.run"++ "network.machines.manage"]}],@@@ -737,7 +698,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/actor.skill-run-request.v1"++ "$ref": "#/components/schemas/bot.machine-manage-request.v1"}}}@@@ -758,7 +719,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/actor.skill-run-result.v1"++ "$ref": "#/components/schemas/network.machine-manage-result.v1"}}}@@@ -776,112 +737,46 @@}}},-- "/api/v1/me/consent": {-- "get": {-- "operationId": "get_api_v1_me_consent",-- "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",-- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",++ "/api/v1/machines/{id}/roles": {++ "put": {++ "operationId": "put_api_v1_machines_id_roles",++ "summary": "Pair, rename, give roles to and revoke a person's own machines.",++ "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.","tags": [-- "network.consent"++ "network.machines"],"x-openvibe-capabilities": [-- "network.consent.manage"++ "network.machines.manage"],"x-openvibe-visibility": ["public"],-- "x-openvibe-quota-class": "standard",++ "x-openvibe-quota-class": "network-write","security": [{"openvibeToken": [-- "network.consent.manage"++ "network.machines.manage"]}],-- "x-openvibe-input": {-- "$ref": "#/components/schemas/network.consent-choice.v1"++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/bot.machine-manage-request.v1"++ }++ }++ }},"parameters": [{-- "name": "on",-- "in": "query",++ "name": "id",++ "in": "path","required": true,"schema": {-- "type": "boolean"-- }-- },-- {-- "name": "kinds",-- "in": "query",-- "required": false,-- "schema": {-- "type": "array",-- "maxItems": 8,-- "uniqueItems": true,-- "items": {-- "enum": [-- "streams",-- "vods",-- "clips",-- "voice",-- "chat",-- "posts",-- "pictures",-- "code"-- ]-- }-- }-- },-- {-- "name": "options",-- "in": "query",-- "required": false,-- "schema": {-- "type": "object",-- "additionalProperties": false,-- "properties": {-- "licence": {-- "enum": [-- "cc-by-4.0",-- "cc0-1.0"-- ]-- },-- "features": {-- "type": "array",-- "maxItems": 4,-- "uniqueItems": true,-- "items": {-- "enum": [-- "verify",-- "find",-- "cast",-- "library"-- ]-- }-- }-- }++ "type": "string"}-- },-- {-- "name": "text_version",-- "in": "query",-- "required": false,-- "schema": {-- "type": "string",-- "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",-- "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."-- },-- "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."-- },-- {-- "name": "adult",-- "in": "query",-- "required": false,-- "schema": {-- "type": "boolean",-- "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."-- },-- "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."}],"responses": {@@@ -890,7 +785,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/network.me-consent-result.v1"++ "$ref": "#/components/schemas/network.machine-manage-result.v1"}}}@@@ -908,25 +803,25 @@}}},-- "/api/v1/me/consent/{purpose}": {-- "put": {-- "operationId": "put_api_v1_me_consent_purpose",-- "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",-- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",++ "/api/v1/machines/pairings": {++ "post": {++ "operationId": "post_api_v1_machines_pairings",++ "summary": "Pair, rename, give roles to and revoke a person's own machines.",++ "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.","tags": [-- "network.consent"++ "network.machines"],"x-openvibe-capabilities": [-- "network.consent.manage"++ "network.machines.manage"],"x-openvibe-visibility": ["public"],-- "x-openvibe-quota-class": "standard",++ "x-openvibe-quota-class": "network-write","security": [{"openvibeToken": [-- "network.consent.manage"++ "network.machines.manage"]}],@@@ -935,28 +830,18 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/network.consent-choice.v1"++ "$ref": "#/components/schemas/bot.machine-manage-request.v1"}}}},-- "parameters": [-- {-- "name": "purpose",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- }-- ],"responses": {"2XX": {"description": "Success","content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/network.me-consent-result.v1"++ "$ref": "#/components/schemas/network.machine-manage-result.v1"}}}@@@ -974,128 +859,72 @@}}},-- "/api/v1/me/consent/{purpose}/{client}": {-- "delete": {-- "operationId": "delete_api_v1_me_consent_purpose_client",-- "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",-- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",++ "/api/v1/machines/roles": {++ "get": {++ "operationId": "get_api_v1_machines_roles",++ "summary": "Read a person's own machines: the list, one machine, and the catalogue of roles.",++ "description": "**network.machines.read** (public): Read a person's own machines: the list, one machine, and the catalogue of roles. A person reads their own with their own token; an app needs the person's consent for this scope (it is a public capability, so it can be granted to an app, and the app then acts only on behalf of the person who consented).","tags": [-- "network.consent"++ "network.machines"],"x-openvibe-capabilities": [-- "network.consent.manage"++ "network.machines.read"],"x-openvibe-visibility": ["public"],-- "x-openvibe-quota-class": "standard",++ "x-openvibe-quota-class": "network-read","security": [{"openvibeToken": [-- "network.consent.manage"++ "network.machines.read"]}],-- "x-openvibe-input": {-- "$ref": "#/components/schemas/network.consent-choice.v1"-- },-- "parameters": [-- {-- "name": "purpose",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/network.machine-list-result.v1"++ }++ }}},-- {-- "name": "client",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }}-- },++ }++ }++ }++ },++ "/api/v1/me/actions": {++ "get": {++ "operationId": "get_api_v1_me_actions",++ "summary": "The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and f",++ "description": "**network.actions.read** (public): The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and failed (network.me-actions-result@1); POST /api/v1/me/actions/:service/:id/:action approves, declines, cancels or retries through the owning authority. Readable from every OpenVibe site with the person's Network token, like the bell.",++ "tags": [++ "network.actions"++ ],++ "x-openvibe-capabilities": [++ "network.actions.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [{-- "name": "on",-- "in": "query",-- "required": true,-- "schema": {-- "type": "boolean"-- }-- },-- {-- "name": "kinds",-- "in": "query",-- "required": false,-- "schema": {-- "type": "array",-- "maxItems": 8,-- "uniqueItems": true,-- "items": {-- "enum": [-- "streams",-- "vods",-- "clips",-- "voice",-- "chat",-- "posts",-- "pictures",-- "code"-- ]-- }-- }-- },-- {-- "name": "options",-- "in": "query",-- "required": false,-- "schema": {-- "type": "object",-- "additionalProperties": false,-- "properties": {-- "licence": {-- "enum": [-- "cc-by-4.0",-- "cc0-1.0"-- ]-- },-- "features": {-- "type": "array",-- "maxItems": 4,-- "uniqueItems": true,-- "items": {-- "enum": [-- "verify",-- "find",-- "cast",-- "library"-- ]-- }-- }-- }-- }-- },-- {-- "name": "text_version",-- "in": "query",-- "required": false,-- "schema": {-- "type": "string",-- "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",-- "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."-- },-- "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."-- },-- {-- "name": "adult",-- "in": "query",-- "required": false,-- "schema": {-- "type": "boolean",-- "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."-- },-- "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."++ "openvibeToken": [++ "network.actions.read"++ ]}],"responses": {@@@ -1104,7 +933,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/network.me-consent-result.v1"++ "$ref": "#/components/schemas/network.me-actions-result.v1"}}}@@@ -1122,16 +951,16 @@}}},-- "/api/v1/me/consent/{purpose}/{client}/refusal": {-- "delete": {-- "operationId": "delete_api_v1_me_consent_purpose_client_refusal",-- "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",-- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",++ "/api/v1/me/actions/{service}/{id}/{action}": {++ "post": {++ "operationId": "post_api_v1_me_actions_service_id_action",++ "summary": "The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and f",++ "description": "**network.actions.read** (public): The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and failed (network.me-actions-result@1); POST /api/v1/me/actions/:service/:id/:action approves, declines, cancels or retries through the owning authority. Readable from every OpenVibe site with the person's Network token, like the bell.","tags": [-- "network.consent"++ "network.actions"],"x-openvibe-capabilities": [-- "network.consent.manage"++ "network.actions.read"],"x-openvibe-visibility": ["public"@@@ -1140,16 +969,13 @@"security": [{"openvibeToken": [-- "network.consent.manage"++ "network.actions.read"]}],-- "x-openvibe-input": {-- "$ref": "#/components/schemas/network.consent-choice.v1"-- },"parameters": [{-- "name": "purpose",++ "name": "service","in": "path","required": true,"schema": {@@@ -1157,7 +983,7 @@}},{-- "name": "client",++ "name": "id","in": "path","required": true,"schema": {@@@ -1165,85 +991,78 @@}},{-- "name": "on",-- "in": "query",++ "name": "action",++ "in": "path","required": true,"schema": {-- "type": "boolean"++ "type": "string"}-- },-- {-- "name": "kinds",-- "in": "query",-- "required": false,-- "schema": {-- "type": "array",-- "maxItems": 8,-- "uniqueItems": true,-- "items": {-- "enum": [-- "streams",-- "vods",-- "clips",-- "voice",-- "chat",-- "posts",-- "pictures",-- "code"-- ]++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/network.me-actions-result.v1"++ }}}},-- {-- "name": "options",-- "in": "query",-- "required": false,-- "schema": {-- "type": "object",-- "additionalProperties": false,-- "properties": {-- "licence": {-- "enum": [-- "cc-by-4.0",-- "cc0-1.0"-- ]-- },-- "features": {-- "type": "array",-- "maxItems": 4,-- "uniqueItems": true,-- "items": {-- "enum": [-- "verify",-- "find",-- "cast",-- "library"-- ]-- }++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"}}}-- },++ }++ }++ }++ },++ "/api/v1/me/actions/skills/{id}/run": {++ "post": {++ "operationId": "post_api_v1_me_actions_skills_id_run",++ "summary": "Start one of Actor's catalogue skills as the signed-in person (plan 2026-11-09 §7): POST /api/v1/me/actions/skills/:id/run asks Actor to run that skill for them, with the input and the choice.",++ "description": "**network.skill.run** (public): Start one of Actor's catalogue skills as the signed-in person (plan 2026-11-09 §7): POST /api/v1/me/actions/skills/:id/run asks Actor to run that skill for them, with the input and the choice. Network never runs the skill itself; Actor does, under the person's own approvals and budget.",++ "tags": [++ "network.skill"++ ],++ "x-openvibe-capabilities": [++ "network.skill.run"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [{-- "name": "text_version",-- "in": "query",-- "required": false,-- "schema": {-- "type": "string",-- "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",-- "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."-- },-- "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."-- },++ "openvibeToken": [++ "network.skill.run"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/actor.skill-run-request.v1"++ }++ }++ }++ },++ "parameters": [{-- "name": "adult",-- "in": "query",-- "required": false,++ "name": "id",++ "in": "path",++ "required": true,"schema": {-- "type": "boolean",-- "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."-- },-- "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."++ "type": "string"++ }}],"responses": {@@@ -1252,7 +1071,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/network.me-consent-result.v1"++ "$ref": "#/components/schemas/actor.skill-run-result.v1"}}}@@@ -1270,9 +1089,9 @@}}},-- "/api/v1/me/consent/apps": {++ "/api/v1/me/consent": {"get": {-- "operationId": "get_api_v1_me_consent_apps",++ "operationId": "get_api_v1_me_consent","summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or","description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.","tags": [@@@ -1402,38 +1221,55 @@}}},-- "/api/v1/me/emails": {-- "get": {-- "operationId": "get_api_v1_me_emails",-- "summary": "Read the one verified email address a person has shared with this app, or check whether one supplied address is verified for a supplied subject.",-- "description": "**identity.emails.read** (public): Read the one verified email address a person has shared with this app, or check whether one supplied address is verified for a supplied subject. The full address list is only available to the person in their own account hub.",++ "/api/v1/me/consent/{purpose}": {++ "put": {++ "operationId": "put_api_v1_me_consent_purpose",++ "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",++ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.","tags": [-- "identity.emails"++ "network.consent"],"x-openvibe-capabilities": [-- "identity.emails.read"++ "network.consent.manage"],"x-openvibe-visibility": ["public"],-- "x-openvibe-quota-class": "identity-read",++ "x-openvibe-quota-class": "standard","security": [{"openvibeToken": [-- "identity.emails.read"++ "network.consent.manage"]}],-- "x-openvibe-input": {-- "$ref": "#/components/schemas/identity.emails-request.v1"++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/network.consent-choice.v1"++ }++ }++ }},++ "parameters": [++ {++ "name": "purpose",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],"responses": {"2XX": {"description": "Success","content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/identity.emails.v1"++ "$ref": "#/components/schemas/network.me-consent-result.v1"}}}@@@ -1451,119 +1287,140 @@}}},-- "/api/v1/me/trust": {-- "get": {-- "operationId": "get_api_v1_me_trust",-- "summary": "Read privacy-limited trust signals for an account or guest by Network subject.",-- "description": "**identity.trust.read** (public): Read privacy-limited trust signals for an account or guest by Network subject. The caller receives only age bands, booleans and aggregate counts, never an IP, email, last-seen timestamp, service user id or linked account identities. Services may use the capability; an app needs the owner's explicit capability grant.",++ "/api/v1/me/consent/{purpose}/{client}": {++ "delete": {++ "operationId": "delete_api_v1_me_consent_purpose_client",++ "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",++ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.","tags": [-- "identity.trust"++ "network.consent"],"x-openvibe-capabilities": [-- "identity.trust.read"++ "network.consent.manage"],"x-openvibe-visibility": ["public"],-- "x-openvibe-quota-class": "identity-read",++ "x-openvibe-quota-class": "standard","security": [{"openvibeToken": [-- "identity.trust.read"++ "network.consent.manage"]}],"x-openvibe-input": {-- "$ref": "#/components/schemas/identity.trust-request.v1"++ "$ref": "#/components/schemas/network.consent-choice.v1"},-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/identity.trust.v1"-- }++ "parameters": [++ {++ "name": "purpose",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "client",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "on",++ "in": "query",++ "required": true,++ "schema": {++ "type": "boolean"++ }++ },++ {++ "name": "kinds",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 8,++ "uniqueItems": true,++ "items": {++ "enum": [++ "streams",++ "vods",++ "clips",++ "voice",++ "chat",++ "posts",++ "pictures",++ "code"++ ]}}},-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"++ {++ "name": "options",++ "in": "query",++ "required": false,++ "schema": {++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "licence": {++ "enum": [++ "cc-by-4.0",++ "cc0-1.0"++ ]++ },++ "features": {++ "type": "array",++ "maxItems": 4,++ "uniqueItems": true,++ "items": {++ "enum": [++ "verify",++ "find",++ "cast",++ "library"++ ]++ }}}}-- }-- }-- }-- },-- "/api/v1/node/self/capabilities": {-- "put": {-- "operationId": "put_api_v1_node_self_capabilities",-- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",-- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",-- "tags": [-- "network.node"-- ],-- "x-openvibe-capabilities": [-- "network.node.self.manage"-- ],-- "x-openvibe-visibility": [-- "internal"-- ],-- "x-openvibe-quota-class": "admin",-- "security": [++ },{-- "openvibeToken": [-- "network.node.self.manage"-- ]++ "name": "text_version",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",++ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."++ },++ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."++ },++ {++ "name": "adult",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."++ },++ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."}],"responses": {"2XX": {-- "description": "Success"-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",++ "description": "Success","content": {-- "application/problem+json": {++ "application/json": {"schema": {-- "$ref": "#/components/schemas/errors.problem.v1"++ "$ref": "#/components/schemas/network.me-consent-result.v1"}}}-- }-- }-- }-- },-- "/api/v1/node/self/credential": {-- "post": {-- "operationId": "post_api_v1_node_self_credential",-- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",-- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",-- "tags": [-- "network.node"-- ],-- "x-openvibe-capabilities": [-- "network.node.self.manage"-- ],-- "x-openvibe-visibility": [-- "internal"-- ],-- "x-openvibe-quota-class": "admin",-- "security": [-- {-- "openvibeToken": [-- "network.node.self.manage"-- ]-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success"},"default": {"description": "An error (RFC 9457 problem details)",@@@ -1578,41 +1435,140 @@}}},-- "/api/v1/node/self/pools/{id}": {-- "get": {-- "operationId": "get_api_v1_node_self_pools_id",-- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",-- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",++ "/api/v1/me/consent/{purpose}/{client}/refusal": {++ "delete": {++ "operationId": "delete_api_v1_me_consent_purpose_client_refusal",++ "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",++ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.","tags": [-- "network.node"++ "network.consent"],"x-openvibe-capabilities": [-- "network.node.self.manage"++ "network.consent.manage"],"x-openvibe-visibility": [-- "internal"++ "public"],-- "x-openvibe-quota-class": "admin",++ "x-openvibe-quota-class": "standard","security": [{"openvibeToken": [-- "network.node.self.manage"++ "network.consent.manage"]}],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/network.consent-choice.v1"++ },"parameters": [{-- "name": "id",++ "name": "purpose",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "client","in": "path","required": true,"schema": {"type": "string"}++ },++ {++ "name": "on",++ "in": "query",++ "required": true,++ "schema": {++ "type": "boolean"++ }++ },++ {++ "name": "kinds",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 8,++ "uniqueItems": true,++ "items": {++ "enum": [++ "streams",++ "vods",++ "clips",++ "voice",++ "chat",++ "posts",++ "pictures",++ "code"++ ]++ }++ }++ },++ {++ "name": "options",++ "in": "query",++ "required": false,++ "schema": {++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "licence": {++ "enum": [++ "cc-by-4.0",++ "cc0-1.0"++ ]++ },++ "features": {++ "type": "array",++ "maxItems": 4,++ "uniqueItems": true,++ "items": {++ "enum": [++ "verify",++ "find",++ "cast",++ "library"++ ]++ }++ }++ }++ }++ },++ {++ "name": "text_version",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",++ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."++ },++ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."++ },++ {++ "name": "adult",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean",++ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."++ },++ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."}],"responses": {"2XX": {-- "description": "Success"++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/network.me-consent-result.v1"++ }++ }++ }},"default": {"description": "An error (RFC 9457 problem details)",@@@ -1627,90 +1583,124 @@}}},-- "/api/v1/node/self/pools/{id}/approve": {-- "post": {-- "operationId": "post_api_v1_node_self_pools_id_approve",-- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",-- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",++ "/api/v1/me/consent/apps": {++ "get": {++ "operationId": "get_api_v1_me_consent_apps",++ "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",++ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.","tags": [-- "network.node"++ "network.consent"],"x-openvibe-capabilities": [-- "network.node.self.manage"++ "network.consent.manage"],"x-openvibe-visibility": [-- "internal"++ "public"],-- "x-openvibe-quota-class": "admin",++ "x-openvibe-quota-class": "standard","security": [{"openvibeToken": [-- "network.node.self.manage"++ "network.consent.manage"]}],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/network.consent-choice.v1"++ },"parameters": [{-- "name": "id",-- "in": "path",++ "name": "on",++ "in": "query","required": true,"schema": {-- "type": "string"++ "type": "boolean"}-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success"},-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"++ {++ "name": "kinds",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "maxItems": 8,++ "uniqueItems": true,++ "items": {++ "enum": [++ "streams",++ "vods",++ "clips",++ "voice",++ "chat",++ "posts",++ "pictures",++ "code"++ ]++ }++ }++ },++ {++ "name": "options",++ "in": "query",++ "required": false,++ "schema": {++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "licence": {++ "enum": [++ "cc-by-4.0",++ "cc0-1.0"++ ]++ },++ "features": {++ "type": "array",++ "maxItems": 4,++ "uniqueItems": true,++ "items": {++ "enum": [++ "verify",++ "find",++ "cast",++ "library"++ ]++ }}}}-- }-- }-- }-- },-- "/api/v1/node/self/pools/{id}/leave": {-- "post": {-- "operationId": "post_api_v1_node_self_pools_id_leave",-- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",-- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",-- "tags": [-- "network.node"-- ],-- "x-openvibe-capabilities": [-- "network.node.self.manage"-- ],-- "x-openvibe-visibility": [-- "internal"-- ],-- "x-openvibe-quota-class": "admin",-- "security": [++ },{-- "openvibeToken": [-- "network.node.self.manage"-- ]-- }-- ],-- "parameters": [++ "name": "text_version",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",++ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."++ },++ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."++ },{-- "name": "id",-- "in": "path",-- "required": true,++ "name": "adult",++ "in": "query",++ "required": false,"schema": {-- "type": "string"-- }++ "type": "boolean",++ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."++ },++ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."}],"responses": {"2XX": {-- "description": "Success"++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/network.me-consent-result.v1"++ }++ }++ }},"default": {"description": "An error (RFC 9457 problem details)",@@@ -1725,41 +1715,41 @@}}},-- "/api/v1/node/self/pools/{id}/status": {-- "post": {-- "operationId": "post_api_v1_node_self_pools_id_status",-- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",-- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",++ "/api/v1/me/emails": {++ "get": {++ "operationId": "get_api_v1_me_emails",++ "summary": "Read the one verified email address a person has shared with this app, or check whether one supplied address is verified for a supplied subject.",++ "description": "**identity.emails.read** (public): Read the one verified email address a person has shared with this app, or check whether one supplied address is verified for a supplied subject. The full address list is only available to the person in their own account hub.","tags": [-- "network.node"++ "identity.emails"],"x-openvibe-capabilities": [-- "network.node.self.manage"++ "identity.emails.read"],"x-openvibe-visibility": [-- "internal"++ "public"],-- "x-openvibe-quota-class": "admin",++ "x-openvibe-quota-class": "identity-read","security": [{"openvibeToken": [-- "network.node.self.manage"++ "identity.emails.read"]}],-- "parameters": [-- {-- "name": "id",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- }-- ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/identity.emails-request.v1"++ },"responses": {"2XX": {-- "description": "Success"++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/identity.emails.v1"++ }++ }++ }},"default": {"description": "An error (RFC 9457 problem details)",@@@ -1774,31 +1764,41 @@}}},-- "/api/v1/node/self/pools/announce": {-- "post": {-- "operationId": "post_api_v1_node_self_pools_announce",-- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",-- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",++ "/api/v1/me/trust": {++ "get": {++ "operationId": "get_api_v1_me_trust",++ "summary": "Read privacy-limited trust signals for an account or guest by Network subject.",++ "description": "**identity.trust.read** (public): Read privacy-limited trust signals for an account or guest by Network subject. The caller receives only age bands, booleans and aggregate counts, never an IP, email, last-seen timestamp, service user id or linked account identities. Services may use the capability; an app needs the owner's explicit capability grant.","tags": [-- "network.node"++ "identity.trust"],"x-openvibe-capabilities": [-- "network.node.self.manage"++ "identity.trust.read"],"x-openvibe-visibility": [-- "internal"++ "public"],-- "x-openvibe-quota-class": "admin",++ "x-openvibe-quota-class": "identity-read","security": [{"openvibeToken": [-- "network.node.self.manage"++ "identity.trust.read"]}],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/identity.trust-request.v1"++ },"responses": {"2XX": {-- "description": "Success"++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/identity.trust.v1"++ }++ }++ }},"default": {"description": "An error (RFC 9457 problem details)",@@@ -2925,35 +2925,228 @@}}},-- "/internal/integrations/github-token": {-- "get": {-- "operationId": "get_internal_integrations_github_token",-- "summary": "Read the network's GitHub API token (read-only, public repositories), which the owner sets in Network's admin panel or in GITHUB_TOKEN.",-- "description": "**network.integration.github.read** (first-party): Read the network's GitHub API token (read-only, public repositories), which the owner sets in Network's admin panel or in GITHUB_TOKEN. Used by OpenVibe.Blog's network changelog so its GitHub calls do not share the host's anonymous rate limit. Never granted to apps or mods.",++ "/internal/integrations/github-token": {++ "get": {++ "operationId": "get_internal_integrations_github_token",++ "summary": "Read the network's GitHub API token (read-only, public repositories), which the owner sets in Network's admin panel or in GITHUB_TOKEN.",++ "description": "**network.integration.github.read** (first-party): Read the network's GitHub API token (read-only, public repositories), which the owner sets in Network's admin panel or in GITHUB_TOKEN. Used by OpenVibe.Blog's network changelog so its GitHub calls do not share the host's anonymous rate limit. Never granted to apps or mods.",++ "tags": [++ "network.integration"++ ],++ "x-openvibe-capabilities": [++ "network.integration.github.read"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "integration-secret",++ "security": [++ {++ "openvibeToken": [++ "network.integration.github.read"++ ]++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/network.github-token-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/link-pairings": {++ "post": {++ "operationId": "post_internal_link_pairings",++ "summary": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired.",++ "description": "**network.link.manage** (internal): A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",++ "tags": [++ "network.link"++ ],++ "x-openvibe-capabilities": [++ "network.link.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "admin",++ "security": [++ {++ "openvibeToken": [++ "network.link.manage"++ ]++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/link-principals/{id}": {++ "get": {++ "operationId": "get_internal_link_principals_id",++ "summary": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired.",++ "description": "**network.link.manage** (internal): A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",++ "tags": [++ "network.link"++ ],++ "x-openvibe-capabilities": [++ "network.link.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "admin",++ "security": [++ {++ "openvibeToken": [++ "network.link.manage"++ ]++ }++ ],++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/link-principals/{id}/revoke": {++ "post": {++ "operationId": "post_internal_link_principals_id_revoke",++ "summary": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired.",++ "description": "**network.link.manage** (internal): A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",++ "tags": [++ "network.link"++ ],++ "x-openvibe-capabilities": [++ "network.link.manage"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "admin",++ "security": [++ {++ "openvibeToken": [++ "network.link.manage"++ ]++ }++ ],++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success"++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/internal/links/report": {++ "post": {++ "operationId": "post_internal_links_report",++ "summary": "Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",++ "description": "**network.link.report** (internal): Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).","tags": [-- "network.integration"++ "network.link"],"x-openvibe-capabilities": [-- "network.integration.github.read"++ "network.link.report"],"x-openvibe-visibility": [-- "first-party"++ "internal"],-- "x-openvibe-quota-class": "integration-secret",++ "x-openvibe-quota-class": "admin","security": [{"openvibeToken": [-- "network.integration.github.read"++ "network.link.report"]}],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/network.link-report-request.v1"++ }++ }++ }++ },"responses": {"2XX": {"description": "Success","content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/network.github-token-result.v1"++ "$ref": "#/components/schemas/network.link-list-result.v1"}}}@@@ -3299,248 +3492,17 @@],"requestBody": {"required": false,-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/network.mod-install-request.v1"-- }-- }-- }-- },-- "parameters": [-- {-- "name": "mod_id",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/network.mod-principal.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/internal/modules/{namespace}/{subject}": {-- "get": {-- "operationId": "get_internal_modules_namespace_subject",-- "summary": "Read a subject's user-module record in a granted namespace.",-- "description": "**network.modules.read** (first-party): Read a subject's user-module record in a granted namespace.",-- "tags": [-- "network.modules"-- ],-- "x-openvibe-capabilities": [-- "network.modules.read"-- ],-- "x-openvibe-visibility": [-- "first-party"-- ],-- "x-openvibe-quota-class": "modules-read",-- "security": [-- {-- "openvibeToken": [-- "network.modules.read"-- ]-- }-- ],-- "parameters": [-- {-- "name": "namespace",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- },-- {-- "name": "subject",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/modules.module-record.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- },-- "put": {-- "operationId": "put_internal_modules_namespace_subject",-- "summary": "Write a subject's record in a namespace the service owns (If-Match revision).",-- "description": "**network.modules.write** (first-party): Write a subject's record in a namespace the service owns (If-Match revision).",-- "tags": [-- "network.modules"-- ],-- "x-openvibe-capabilities": [-- "network.modules.write"-- ],-- "x-openvibe-visibility": [-- "first-party"-- ],-- "x-openvibe-quota-class": "modules-write",-- "security": [-- {-- "openvibeToken": [-- "network.modules.write"-- ]-- }-- ],-- "requestBody": {-- "required": false,-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/network.module-write-request.v1"-- }-- }-- }-- },-- "parameters": [-- {-- "name": "namespace",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- },-- {-- "name": "subject",-- "in": "path",-- "required": true,-- "schema": {-- "type": "string"-- }-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success",-- "content": {-- "application/json": {-- "schema": {-- "$ref": "#/components/schemas/modules.module-record.v1"-- }-- }-- }-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/internal/node-pairings": {-- "post": {-- "operationId": "post_internal_node_pairings",-- "summary": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired.",-- "description": "**network.node.manage** (internal): A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",-- "tags": [-- "network.node"-- ],-- "x-openvibe-capabilities": [-- "network.node.manage"-- ],-- "x-openvibe-visibility": [-- "internal"-- ],-- "x-openvibe-quota-class": "admin",-- "security": [-- {-- "openvibeToken": [-- "network.node.manage"-- ]-- }-- ],-- "responses": {-- "2XX": {-- "description": "Success"-- },-- "default": {-- "description": "An error (RFC 9457 problem details)",-- "content": {-- "application/problem+json": {-- "schema": {-- "$ref": "#/components/schemas/errors.problem.v1"-- }-- }-- }-- }-- }-- }-- },-- "/internal/node-principals/{id}": {-- "get": {-- "operationId": "get_internal_node_principals_id",-- "summary": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired.",-- "description": "**network.node.manage** (internal): A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",-- "tags": [-- "network.node"-- ],-- "x-openvibe-capabilities": [-- "network.node.manage"-- ],-- "x-openvibe-visibility": [-- "internal"-- ],-- "x-openvibe-quota-class": "admin",-- "security": [-- {-- "openvibeToken": [-- "network.node.manage"-- ]++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/network.mod-install-request.v1"++ }++ }}-- ],++ },"parameters": [{-- "name": "id",++ "name": "mod_id","in": "path","required": true,"schema": {@@@ -3550,7 +3512,14 @@],"responses": {"2XX": {-- "description": "Success"++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/network.mod-principal.v1"++ }++ }++ }},"default": {"description": "An error (RFC 9457 problem details)",@@@ -3565,31 +3534,39 @@}}},-- "/internal/node-principals/{id}/revoke": {-- "post": {-- "operationId": "post_internal_node_principals_id_revoke",-- "summary": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired.",-- "description": "**network.node.manage** (internal): A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",++ "/internal/modules/{namespace}/{subject}": {++ "get": {++ "operationId": "get_internal_modules_namespace_subject",++ "summary": "Read a subject's user-module record in a granted namespace.",++ "description": "**network.modules.read** (first-party): Read a subject's user-module record in a granted namespace.","tags": [-- "network.node"++ "network.modules"],"x-openvibe-capabilities": [-- "network.node.manage"++ "network.modules.read"],"x-openvibe-visibility": [-- "internal"++ "first-party"],-- "x-openvibe-quota-class": "admin",++ "x-openvibe-quota-class": "modules-read","security": [{"openvibeToken": [-- "network.node.manage"++ "network.modules.read"]}],"parameters": [{-- "name": "id",++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "subject","in": "path","required": true,"schema": {@@@ -3599,7 +3576,14 @@],"responses": {"2XX": {-- "description": "Success"++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/modules.module-record.v1"++ }++ }++ }},"default": {"description": "An error (RFC 9457 problem details)",@@@ -3612,27 +3596,25 @@}}}-- }-- },-- "/internal/nodes/report": {-- "post": {-- "operationId": "post_internal_nodes_report",-- "summary": "Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",-- "description": "**network.node.report** (internal): Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",++ },++ "put": {++ "operationId": "put_internal_modules_namespace_subject",++ "summary": "Write a subject's record in a namespace the service owns (If-Match revision).",++ "description": "**network.modules.write** (first-party): Write a subject's record in a namespace the service owns (If-Match revision).","tags": [-- "network.node"++ "network.modules"],"x-openvibe-capabilities": [-- "network.node.report"++ "network.modules.write"],"x-openvibe-visibility": [-- "internal"++ "first-party"],-- "x-openvibe-quota-class": "admin",++ "x-openvibe-quota-class": "modules-write","security": [{"openvibeToken": [-- "network.node.report"++ "network.modules.write"]}],@@@ -3641,18 +3623,36 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/network.node-report-request.v1"++ "$ref": "#/components/schemas/network.module-write-request.v1"}}}},++ "parameters": [++ {++ "name": "namespace",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "subject",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],"responses": {"2XX": {"description": "Success","content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/network.node-list-result.v1"++ "$ref": "#/components/schemas/modules.module-record.v1"}}}@@@ -3952,12 +3952,12 @@"post": {"operationId": "post_internal_registry_instances_report","summary": "Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",-- "description": "**network.node.report** (internal): Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",++ "description": "**network.link.report** (internal): Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).","tags": [-- "network.node"++ "network.link"],"x-openvibe-capabilities": [-- "network.node.report"++ "network.link.report"],"x-openvibe-visibility": ["internal"@@@ -3966,7 +3966,7 @@"security": [{"openvibeToken": [-- "network.node.report"++ "network.link.report"]}],@@@ -3975,7 +3975,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/network.node-report-request.v1"++ "$ref": "#/components/schemas/network.link-report-request.v1"}}}@@@ -3986,7 +3986,7 @@"content": {"application/json": {"schema": {-- "$ref": "#/components/schemas/network.node-list-result.v1"++ "$ref": "#/components/schemas/network.link-list-result.v1"}}}@@@ -6660,73 +6660,236 @@"channel"]},-- "target_id": {++ "target_id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "followers": {++ "type": "integer",++ "minimum": 0++ },++ "following": {++ "type": "boolean",++ "description": "Present for a signed-in caller."++ },++ "notify_email": {++ "type": "boolean"++ },++ "notify_push": {++ "type": "boolean"++ },++ "since": {++ "type": "string",++ "format": "date-time",++ "description": "When the caller started following."++ }++ }++ },++ "network.follow-write-request.v1": {++ "title": "NetworkFollowWriteRequest",++ "description": "network.follow-write-request@1 (ADR-030 step 4; capability network.follows.write): a first-party product records a follow or unfollow on a person's behalf, where they pressed the button. PUT /internal/follows/:type/:target { follower, notify_email?, notify_push? } follows (idempotent; flags change a follow). DELETE /internal/follows/:type/:target?follower=usr_… unfollows (idempotent). Answers network.follow-status-result@1 for the follower. Network emits network.follow.* as for a person's own change.",++ "type": "object",++ "required": [++ "follower"++ ],++ "additionalProperties": false,++ "properties": {++ "follower": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "notify_email": {++ "type": "boolean"++ },++ "notify_push": {++ "type": "boolean"++ }++ }++ },++ "network.github-token-result.v1": {++ "title": "NetworkGithubTokenResult",++ "description": "network.github-token-result@1: the answer of network.integration.github.read on OpenVibe.Network. GET /internal/integrations/github-token (a service token holding the capability; the shared internal key is refused) → { token, source }: the network's read-only GitHub API token and where it is set (GITHUB_TOKEN in the environment, or the value the owner saved in admin). Answered with Cache-Control: no-store; 404 { error: not_configured, detail } when no token is set.",++ "type": "object",++ "required": [++ "token",++ "source"++ ],++ "properties": {++ "token": {++ "type": "string",++ "minLength": 1,++ "description": "A GitHub token (ghp_…, github_pat_…). A secret: never log or show it."++ },++ "source": {++ "enum": [++ "env",++ "database",++ "unset"++ ],++ "description": "env: GITHUB_TOKEN; database: saved in admin (Settings → GitHub)."++ }++ }++ },++ "network.link-list-result.v1": {++ "title": "NetworkLinkListResult",++ "description": "network.link-list-result@1: GET /api/v1/links on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().",++ "type": "object",++ "required": [++ "nodes",++ "generated_at"++ ],++ "additionalProperties": false,++ "properties": {++ "nodes": {++ "type": "array",++ "items": {++ "$ref": "#/components/schemas/network.link.v1"++ }++ },++ "generated_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ },++ "network.link-report-request.v1": {++ "title": "NetworkLinkReportRequest",++ "description": "network.link-report-request@1: POST /internal/links/report on OpenVibe.Network (capability network.link.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.",++ "type": "object",++ "required": [++ "source",++ "nodes"++ ],++ "additionalProperties": false,++ "properties": {++ "source": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$",++ "description": "The reporting inventory (e.g. the host id)."++ },++ "nodes": {++ "type": "array",++ "maxItems": 500,++ "items": {++ "$ref": "#/components/schemas/network.link.v1"++ }++ }++ }++ },++ "network.link.v1": {++ "title": "NetworkLink",++ "description": "network.link@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.",++ "type": "object",++ "required": [++ "id",++ "roles",++ "location",++ "health",++ "updated_at"++ ],++ "additionalProperties": false,++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "name": {++ "type": "string",++ "maxLength": 80++ },++ "roles": {++ "type": "array",++ "minItems": 1,++ "uniqueItems": true,++ "items": {++ "enum": [++ "web",++ "app",++ "data",++ "media-worker",++ "ingest",++ "edge-probe",++ "edge-relay",++ "edge-cache",++ "gpu",++ "staging",++ "ci"++ ]++ }++ },++ "location": {++ "type": "object",++ "required": [++ "region",++ "country"++ ],++ "additionalProperties": false,++ "properties": {++ "region": {++ "type": "string",++ "pattern": "^[a-z]{2}-[a-z]+(-[0-9])?$",++ "description": "e.g. us-west, eu-central"++ },++ "country": {++ "type": "string",++ "pattern": "^[A-Z]{2}$"++ },++ "city": {++ "type": "string",++ "maxLength": 80++ },++ "lat": {++ "type": "number",++ "minimum": -90,++ "maximum": 90++ },++ "lon": {++ "type": "number",++ "minimum": -180,++ "maximum": 180++ }++ }++ },++ "provider": {"type": "string",-- "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"-- },-- "followers": {-- "type": "integer",-- "minimum": 0-- },-- "following": {-- "type": "boolean",-- "description": "Present for a signed-in caller."-- },-- "notify_email": {-- "type": "boolean"-- },-- "notify_push": {-- "type": "boolean"++ "maxLength": 40},-- "since": {-- "type": "string",-- "format": "date-time",-- "description": "When the caller started following."-- }-- }-- },-- "network.follow-write-request.v1": {-- "title": "NetworkFollowWriteRequest",-- "description": "network.follow-write-request@1 (ADR-030 step 4; capability network.follows.write): a first-party product records a follow or unfollow on a person's behalf, where they pressed the button. PUT /internal/follows/:type/:target { follower, notify_email?, notify_push? } follows (idempotent; flags change a follow). DELETE /internal/follows/:type/:target?follower=usr_… unfollows (idempotent). Answers network.follow-status-result@1 for the follower. Network emits network.follow.* as for a person's own change.",-- "type": "object",-- "required": [-- "follower"-- ],-- "additionalProperties": false,-- "properties": {-- "follower": {++ "beacon": {"type": "string",-- "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ "format": "uri",++ "pattern": "^https://",++ "description": "Timed by clients (GET, tiny body, no caching) to measure round-trip time."},-- "notify_email": {-- "type": "boolean"++ "health": {++ "type": "object",++ "required": [++ "status",++ "checked_at"++ ],++ "additionalProperties": false,++ "properties": {++ "status": {++ "enum": [++ "up",++ "degraded",++ "down",++ "unknown"++ ]++ },++ "checked_at": {++ "type": "string",++ "format": "date-time"++ }++ }},-- "notify_push": {-- "type": "boolean"-- }-- }-- },-- "network.github-token-result.v1": {-- "title": "NetworkGithubTokenResult",-- "description": "network.github-token-result@1: the answer of network.integration.github.read on OpenVibe.Network. GET /internal/integrations/github-token (a service token holding the capability; the shared internal key is refused) → { token, source }: the network's read-only GitHub API token and where it is set (GITHUB_TOKEN in the environment, or the value the owner saved in admin). Answered with Cache-Control: no-store; 404 { error: not_configured, detail } when no token is set.",-- "type": "object",-- "required": [-- "token",-- "source"-- ],-- "properties": {-- "token": {++ "updated_at": {"type": "string",-- "minLength": 1,-- "description": "A GitHub token (ghp_…, github_pat_…). A secret: never log or show it."++ "format": "date-time"},-- "source": {-- "enum": [-- "env",-- "database",-- "unset"-- ],-- "description": "env: GITHUB_TOKEN; database: saved in admin (Settings → GitHub)."++ "cell": {++ "type": "string",++ "pattern": "^[a-z]{2,8}-[0-9]{1,3}$",++ "description": "Cell the node belongs to, e.g. wnam-1."}}},@@@ -6781,8 +6944,8 @@},"principal_id": {"type": "string",-- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",-- "description": "The machine's node principal on Network (nod_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for."++ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The machine's node principal on Network (lnk_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for."},"name": {"type": "string",@@@ -7286,169 +7449,6 @@}]},-- "network.node-list-result.v1": {-- "title": "NetworkNodeListResult",-- "description": "network.node-list-result@1: GET /api/v1/nodes on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().",-- "type": "object",-- "required": [-- "nodes",-- "generated_at"-- ],-- "additionalProperties": false,-- "properties": {-- "nodes": {-- "type": "array",-- "items": {-- "$ref": "#/components/schemas/network.node.v1"-- }-- },-- "generated_at": {-- "type": "string",-- "format": "date-time"-- }-- }-- },-- "network.node-report-request.v1": {-- "title": "NetworkNodeReportRequest",-- "description": "network.node-report-request@1: POST /internal/nodes/report on OpenVibe.Network (capability network.node.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.",-- "type": "object",-- "required": [-- "source",-- "nodes"-- ],-- "additionalProperties": false,-- "properties": {-- "source": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9-]{1,39}$",-- "description": "The reporting inventory (e.g. the host id)."-- },-- "nodes": {-- "type": "array",-- "maxItems": 500,-- "items": {-- "$ref": "#/components/schemas/network.node.v1"-- }-- }-- }-- },-- "network.node.v1": {-- "title": "NetworkNode",-- "description": "network.node@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.",-- "type": "object",-- "required": [-- "id",-- "roles",-- "location",-- "health",-- "updated_at"-- ],-- "additionalProperties": false,-- "properties": {-- "id": {-- "type": "string",-- "pattern": "^[a-z][a-z0-9-]{1,39}$"-- },-- "name": {-- "type": "string",-- "maxLength": 80-- },-- "roles": {-- "type": "array",-- "minItems": 1,-- "uniqueItems": true,-- "items": {-- "enum": [-- "web",-- "app",-- "data",-- "media-worker",-- "ingest",-- "edge-probe",-- "edge-relay",-- "edge-cache",-- "gpu",-- "staging",-- "ci"-- ]-- }-- },-- "location": {-- "type": "object",-- "required": [-- "region",-- "country"-- ],-- "additionalProperties": false,-- "properties": {-- "region": {-- "type": "string",-- "pattern": "^[a-z]{2}-[a-z]+(-[0-9])?$",-- "description": "e.g. us-west, eu-central"-- },-- "country": {-- "type": "string",-- "pattern": "^[A-Z]{2}$"-- },-- "city": {-- "type": "string",-- "maxLength": 80-- },-- "lat": {-- "type": "number",-- "minimum": -90,-- "maximum": 90-- },-- "lon": {-- "type": "number",-- "minimum": -180,-- "maximum": 180-- }-- }-- },-- "provider": {-- "type": "string",-- "maxLength": 40-- },-- "beacon": {-- "type": "string",-- "format": "uri",-- "pattern": "^https://",-- "description": "Timed by clients (GET, tiny body, no caching) to measure round-trip time."-- },-- "health": {-- "type": "object",-- "required": [-- "status",-- "checked_at"-- ],-- "additionalProperties": false,-- "properties": {-- "status": {-- "enum": [-- "up",-- "degraded",-- "down",-- "unknown"-- ]-- },-- "checked_at": {-- "type": "string",-- "format": "date-time"-- }-- }-- },-- "updated_at": {-- "type": "string",-- "format": "date-time"-- },-- "cell": {-- "type": "string",-- "pattern": "^[a-z]{2,8}-[0-9]{1,3}$",-- "description": "Cell the node belongs to, e.g. wnam-1."-- }-- }-- },"network.notification-push-request.v1": {"title": "NetworkNotificationPushRequest","description": "network.notification-push-request@1: bodies of network.notifications.push on OpenVibe.Network (a service token holding the capability). POST /internal/notifications/push: { user_id | subject, type?, title?, message?, … } creates one notification (400 without user_id or subject; 404 for an unknown subject). POST /internal/notifications/push-bulk: { user_ids | subjects, …same fields } creates it for each recipient (400 without a non-empty user_ids or subjects, or with more than 1000). Recipients are Network user ids or canonical usr_ subjects; a subject resolves to its account, so a service that knows the person by subject (Vision's consent request, Cam's event alert) need not hold the numeric id. Unknown fields are ignored; 503 when the notification service is not running. The other /internal/notifications/* routes (unread, mark-read, resolve-users) take the internal key only and are not part of this capability.",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.News API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.372.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": "OpenRestream API",-- "version": "0.371.0",-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Play API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Quest API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Report API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Run API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"@@@ -1973,7 +1973,7 @@"null"],"maxLength": 128,-- "description": "The offer's provider (the usage reading's `provider`); null for a first-party node."++ "description": "The offer's provider (the usage reading's `provider`); null for a first-party machine."},"region": {"type": [@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Search API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Services API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Today API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Vision API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Watch API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"@@@ -3006,7 +3006,7 @@},"watch.watch.v1": {"title": "Watch",-- "description": "watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Node probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.",++ "description": "watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Link probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.","type": "object","additionalProperties": false,"required": [@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Wiki API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Work API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Zone API",-- "version": "0.371.0",-- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",++ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"@@@ -76,7 +76,7 @@ export type ServiceTokenClaims = {*/iss: string;sub: string;-- actor_type: "service" | "app" | "mod" | "node" | "agent";++ actor_type: "service" | "app" | "mod" | "link" | "agent";/*** @minItems 1*/@@@ -159209,9 +159209,9 @@ export type AiCredentialPut = {/*** openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost).*/-- provider: "openai" | "anthropic" | "node";++ provider: "openai" | "anthropic" | "link";/**-- * provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine.++ * provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine.*/machine?: string;base_url?: string;@@@ -159245,7 +159245,7 @@ export type AiCredentialPutV2 = {/*** anthropic = Anthropic's Messages API; openai = any OpenAI-compatible endpoint; deepseek, minimax and openrouter = those providers; custom = the endpoint in base_url (a URL that speaks the OpenAI or the Anthropic format, by the route it is used on); node = the person's own machine (no key, no base_url, no cost).*/-- provider: "anthropic" | "openai" | "deepseek" | "minimax" | "openrouter" | "custom" | "node";++ provider: "anthropic" | "openai" | "deepseek" | "minimax" | "openrouter" | "custom" | "link";/*** The provider key (ai.credential-put@2's name for v1's api_key). Required the first time, and whenever provider or base_url changes (a key is never sent to a new endpoint without being entered again); otherwise the stored key is kept, and it is never returned.*/@@@ -159259,7 +159259,7 @@ export type AiCredentialPutV2 = {*/base_url?: string;/**-- * provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model, or `any` of their own.++ * provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model, or `any` of their own.*/machine?: string;/**@@@ -159290,9 +159290,9 @@ export type AiCredential = {* The service that stored it; only it may use or change it.*/owner: string;-- provider: "openai" | "anthropic" | "node";++ provider: "openai" | "anthropic" | "link";/**-- * provider node: the machine it runs on, or `any` of the person's own (contracts 0.221.0)++ * provider link: the machine it runs on, or `any` of the person's own (contracts 0.221.0)*/machine?: string | null;base_url?: string | null;@@@ -159326,9 +159326,9 @@ export type AiCredentialV2 = {* The service that stored it; only it may use or change it.*/owner: string;-- provider: "anthropic" | "openai" | "deepseek" | "minimax" | "openrouter" | "custom" | "node";++ provider: "anthropic" | "openai" | "deepseek" | "minimax" | "openrouter" | "custom" | "link";/**-- * provider node: the machine it runs on, or `any` of the person's own++ * provider link: the machine it runs on, or `any` of the person's own*/machine?: string | null;base_url?: string | null;@@@ -160444,7 +160444,7 @@ export interface AiInferenceDeployment {*/trust_tier?: "first_party" | "owner_node" | "approved_wholesale" | "unverified";/**-- * Operator slug or `owner:<subject_id>` for a private node.++ * Operator slug or `owner:<subject_id>` for a private machine.*/operator?: string;/**@@@ -197303,7 +197303,7 @@ export interface TradeWatchlist {/** [email protected] (owner: watch) *//**-- * watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Node probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.++ * watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Link probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.*/export interface Watch {id: string;@@@ -210287,11 +210287,11 @@ export interface HostSite {updated_at: string;}--/** [email protected] (owner: network) */++/** [email protected] (owner: network) *//**-- * network.node@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.++ * network.link@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.*/--export interface NetworkNode {++export interface NetworkLink {id: string;name?: string;/**@@@ -210351,11 +210351,11 @@ export interface NetworkNode {cell?: string;}--/** [email protected] (owner: network) */++/** [email protected] (owner: network) *//**-- * network.node-report-request@1: POST /internal/nodes/report on OpenVibe.Network (capability network.node.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.++ * network.link-report-request@1: POST /internal/links/report on OpenVibe.Network (capability network.link.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.*/--export interface NetworkNodeReportRequest {++export interface NetworkLinkReportRequest {/*** The reporting inventory (e.g. the host id).*/@@@ -210363,15 +210363,15 @@ export interface NetworkNodeReportRequest {/*** @maxItems 500*/-- nodes: NetworkNode[];++ nodes: NetworkLink[];}--/** [email protected] (owner: network) */++/** [email protected] (owner: network) *//**-- * network.node-list-result@1: GET /api/v1/nodes on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().++ * network.link-list-result@1: GET /api/v1/links on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().*/--export interface NetworkNodeListResult {-- nodes: NetworkNode[];++export interface NetworkLinkListResult {++ nodes: NetworkLink[];generated_at: string;}@@@ -210385,7 +210385,7 @@ export interface NetworkMachine {*/id: string;/**-- * The machine's node principal on Network (nod_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for.++ * The machine's node principal on Network (lnk_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for.*/principal_id?: string;/**@@@ -210539,7 +210539,7 @@ export interface NetworkMachineRolesPut {/** [email protected] (owner: network) *//**-- * network.machine-pairing@1: a one-time code that pairs a machine with the person's account (POST /api/v1/machines/pairings, capability machines.manage). The code is shown once, expires, and works once: the machine redeems it and becomes the person's. The command is what the owner runs on the machine (openvibe.bot/install). The code is never stored in the clear and never returned again.++ * network.machine-pairing@1: a one-time code that pairs a machine with the person's account (POST /api/v1/machines/pairings, capability machines.manage). The code is shown once, expires, and works once: the machine redeems it and becomes the person's. The command is what the owner runs on the machine (openvibe.link/install). The code is never stored in the clear and never returned again.*/export interface NetworkMachinePairing {/**@@@ -210555,7 +210555,7 @@ export interface NetworkMachinePairing {*/expires_at: string;/**-- * The command the owner runs on the machine (openvibe.bot/install, with the network, the pairing id and the code).++ * The command the owner runs on the machine (openvibe.link/install, with the network, the pairing id and the code).*/installer: string;}@@@ -210596,7 +210596,7 @@ export interface NetworkMachineOfflinePayload {/** [email protected] (owner: network) *//**-- * network.machine.paired v1 (OpenVibe.Network, plan L34: the machines hub). A machine was paired with a person's account: it redeemed the one-time code and became theirs. It holds a credential from then on; the code is spent. The machine's device id in Bot is absent until the machine checks in for the first time. Envelope: subject { type: machine, id: <nod_…> }, visibility internal, actor service:network.++ * network.machine.paired v1 (OpenVibe.Network, plan L34: the machines hub). A machine was paired with a person's account: it redeemed the one-time code and became theirs. It holds a credential from then on; the code is spent. The machine's device id in Bot is absent until the machine checks in for the first time. Envelope: subject { type: machine, id: <lnk_…> }, visibility internal, actor service:network.*/export interface NetworkMachinePairedPayload {/**@@@ -210728,7 +210728,7 @@ export interface NetworkCoinsReadResult {/** [email protected] (owner: network) *//**-- * What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.node@1) deliberately leaves out.++ * What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.link@1) deliberately leaves out.*/export type ResourceOffer = {[k: string]: unknown | undefined;@@@ -210736,7 +210736,7 @@ export type ResourceOffer = {offer_id: string;kind: "node" | "provider" | "storage" | "delivery" | "runtime" | "agent" | "harness";/**-- * network.node@1 id, for kind node++ * network.link@1 id, for kind node*/node_id?: string;/**@@@ -210758,7 +210758,7 @@ export type ResourceOffer = {*/owner?: string;/**-- * 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 Link'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. Beside them, a kind harness names `runtime:<class>` for each platform.runtime-class@1 class it can run (runtime:function, runtime:code, runtime:browser, runtime:linux, runtime:desktop, runtime:gpu); these are ordinary capability names, not reserved, and OpenVibe.Codes already advertises the classes it offers.++ * 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 Link's job worker, one per class: worker:function, worker:code, worker:browser, worker:linux, worker:desktop and worker:gpu ($defs.reservedWorkerCapabilities). A Link advertises the name of each class it runs (Link's status.capabilities.worker); every other `worker:` name (worker:ffmpeg, worker:ai-gpu) is an ordinary capability name. Beside them, a kind harness names `runtime:<class>` for each platform.runtime-class@1 class it can run (runtime:function, runtime:code, runtime:browser, runtime:linux, runtime:desktop, runtime:gpu); these are ordinary capability names, not reserved, and OpenVibe.Codes already advertises the classes it offers.*/capabilities: string[];capacity?: {@@@ -211209,11 +211209,48 @@ export interface TelemetrySample {};}--/** [email protected] (owner: network) */++/** [email protected] (owner: network) *//**-- * Node hardware, placement regions, tags, and operating costs (T1 Universal Fabric; ADR-034 proposed).++ * Where a person's work can run. Only ready candidates may be picked; a missing rental quote is never a price.*/--export interface NodeCapabilities {++export interface LinkCompute {++ sources: {++ id?: string;++ principal_id?: string;++ kind: "personal" | "cloud" | "exchange" | "community";++ name: string;++ status: "ready" | "unavailable" | "coming_soon";++ reason: string | null;++ price?: {++ amount_usd: number;++ unit: "hour";++ } | null;++ quoted_at?: string;++ expires_at?: string;++ }[];++ candidates: ({++ id?: string;++ principal_id?: string;++ kind: "personal" | "cloud" | "exchange" | "community";++ name: string;++ status: "ready" | "unavailable" | "coming_soon";++ reason: string | null;++ price?: {++ amount_usd: number;++ unit: "hour";++ } | null;++ quoted_at?: string;++ expires_at?: string;++ } & {++ id: string;++ })[];++}++++/** [email protected] (owner: network) */++/**++ * Link machine hardware, placement regions, tags, and operating costs (T1 Universal Fabric; ADR-034 proposed).++ */++export interface LinkCapabilities {node_id: string;cpu: {cores: number;@@@ -262643,7 +262680,7 @@ export type BotJobDispatchResult =*/subject: string | null;/**-- * The offer's provider, as Run sent it; null for a first-party node.++ * The offer's provider, as Run sent it; null for a first-party machine.*/provider: string | null;/**@@@ -262733,7 +262770,7 @@ export type BotJobDispatchResult =*/subject: string | null;/**-- * The offer's provider, as Run sent it; null for a first-party node.++ * The offer's provider, as Run sent it; null for a first-party machine.*/provider: string | null;/**@@@ -263337,7 +263374,7 @@ export type RunJobReadResult = {*/node: string;/**-- * The offer's provider (the usage reading's `provider`); null for a first-party node.++ * The offer's provider (the usage reading's `provider`); null for a first-party machine.*/provider: string | null;region: string | null;@@@ -263557,7 +263594,7 @@ export interface RunJobStartedPayload {*/node: string;/**-- * The offer's provider (the usage reading's `provider`); null for a first-party node.++ * The offer's provider (the usage reading's `provider`); null for a first-party machine.*/provider: string | null;region: string | null;@@@ -263617,7 +263654,7 @@ export interface RunJobSucceededPayload {*/node: string;/**-- * The offer's provider (the usage reading's `provider`); null for a first-party node.++ * The offer's provider (the usage reading's `provider`); null for a first-party machine.*/provider: string | null;region: string | null;@@@ -263689,7 +263726,7 @@ export interface RunJobFailedPayload {*/node: string;/**-- * The offer's provider (the usage reading's `provider`); null for a first-party node.++ * The offer's provider (the usage reading's `provider`); null for a first-party machine.*/provider: string | null;region: string | null;@@@ -263764,7 +263801,7 @@ export interface RunJobCancelledPayload {*/node: string;/**-- * The offer's provider (the usage reading's `provider`); null for a first-party node.++ * The offer's provider (the usage reading's `provider`); null for a first-party machine.*/provider: string | null;region: string | null;@@@ -263833,7 +263870,7 @@ export interface RunJobExpiredPayload {*/node: string;/**-- * The offer's provider (the usage reading's `provider`); null for a first-party node.++ * The offer's provider (the usage reading's `provider`); null for a first-party machine.*/provider: string | null;region: string | null;@@@ -263988,11 +264025,11 @@ export interface ResourceControlResult {at: string;}--/** [email protected] (owner: network) */++/** [email protected] (owner: network) *//**-- * network.node.revoked v1 (OpenVibe.Network; plan T2, docs/t2-cells-and-node-principal.md section 10, migrations/0014_node_pairing.sql). A node principal (nod_…), a person's paired machine, was revoked: through POST /internal/node-principals/:id/revoke (network.node.manage, the service that paired it), through POST /api/v1/me/nodes/:id/revoke (the owner's session), or with the owner's account. Network revokes the row and clears the credential and its grace window; a machine holding a live node token is refused at its next use (≤ 300 s), and until this event only learned of the revoke at its next reauth (≤ 330 s). A consumer stops the machine at once; a second revoke changes nothing and emits nothing. The credential and its hash are never carried. Envelope: subject { type: node_principal, id: <nod_…> }, visibility internal, actor the service (svc:<service>) or the owner ({ type: user }) that revoked.++ * network.link.revoked v1 (OpenVibe.Network; plan T2, docs/t2-cells-and-node-principal.md section 10, migrations/0014_node_pairing.sql). A node principal (lnk_…), a person's paired machine, was revoked: through POST /internal/link-principals/:id/revoke (network.link.manage, the service that paired it), through POST /api/v1/me/nodes/:id/revoke (the owner's session), or with the owner's account. Network revokes the row and clears the credential and its grace window; a machine holding a live node token is refused at its next use (≤ 300 s), and until this event only learned of the revoke at its next reauth (≤ 330 s). A consumer stops the machine at once; a second revoke changes nothing and emits nothing. The credential and its hash are never carried. Envelope: subject { type: node_principal, id: <lnk_…> }, visibility internal, actor the service (svc:<service>) or the owner ({ type: user }) that revoked.*/--export interface NetworkNodeRevokedPayload {++export interface NetworkLinkRevokedPayload {/*** The machine's node id (platform_node_principals.node_id), minted with the principal and unique among principals.*/@@@ -294275,7 +294312,7 @@ export interface BenchRun {};executor: {/**-- * codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it.++ * codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it.*/kind: "codes" | "run" | "node" | "submitted";id: string;@@@ -294346,7 +294383,7 @@ export interface BenchRunV2 {};executor: {/**-- * codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it.++ * codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it.*/kind: "codes" | "run" | "node" | "submitted";id: string;@@@ -294807,7 +294844,7 @@ export interface TodayReminderResult {/** [email protected] (owner: connect) *//**-- * website.share@1: one Share on OpenVibe.Website Connect, a local port published at a stable hostname through the edge relay. The record says who owns it, which project scopes it, which Node tunnel serves it, which local target it may reach, which policy version the edge enforces and where it is in its life (draft, review, active, paused, revoked). The tunnel token, the request bodies the inspector captures and the policy's password hash are never part of this document.++ * website.share@1: one Share on OpenVibe.Website Connect, a local port published at a stable hostname through the edge relay. The record says who owns it, which project scopes it, which Link tunnel serves it, which local target it may reach, which policy version the edge enforces and where it is in its life (draft, review, active, paused, revoked). The tunnel token, the request bodies the inspector captures and the policy's password hash are never part of this document.*/export interface WebsiteShare {id: string;@@@ -294861,7 +294898,7 @@ export interface WebsiteShare {*/expires_at?: string | null;/**-- * The last successful Node liveness check; null until the first tunnel.++ * The last successful Link liveness check; null until the first tunnel.*/last_seen_at?: string | null;created_at: string;@@@ -294896,7 +294933,7 @@ export interface WebsiteShareCreateRequest {password_set?: never;};/**-- * Optional: bind the share to this Node principal now. Omit to bind it when the first tunnel connects.++ * Optional: bind the share to this Link principal now. Omit to bind it when the first tunnel connects.*/node_id?: string;/**@@@ -303479,7 +303516,7 @@ export interface ChatParty {/** [email protected] (owner: bot) *//**-- * bot.machine-pool@1 (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC, `rpc-server` on the members and `llama-server --rpc` on the head. OpenVibe.Network holds the record: the owner proposes it on /machines (POST /api/v1/machine-pools, two to eight of their own machines and an optional head; the default head has the most free VRAM), and each machine reads it with its own node token (GET /api/v1/node/self/pools/:id, network.node.self.manage) and approves it locally (POST /api/v1/node/self/pools/:id/approve with { certificate }). A machine's `certificate` is null until it approves on that machine: the Link generates an Ed25519 key there, keeps the private key, and uploads only its self-signed public certificate, which the other members pin for the pool's private link. A pool with any null certificate never serves. The record never carries a private key or a prompt. What a schema cannot say, Network and Node check at run time: the members are distinct and the head is one of them; every member has the pool's owner, today, through Bot; every member was seen from the same public address with a private address in the same subnet; the head's direct link probe to each member passes; each machine's certificate is its own; and any change of membership, owner, head or key revokes the pool, so all machines approve a new one. A machine that leaves (removed, signed out, a different owner) drops its link and its key at once.++ * bot.machine-pool@1 (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC, `rpc-server` on the members and `llama-server --rpc` on the head. OpenVibe.Network holds the record: the owner proposes it on /machines (POST /api/v1/machine-pools, two to eight of their own machines and an optional head; the default head has the most free VRAM), and each machine reads it with its own Link token (GET /api/v1/link/self/pools/:id, network.link.self.manage) and approves it locally (POST /api/v1/link/self/pools/:id/approve with { certificate }). A machine's `certificate` is null until it approves on that machine: the Link generates an Ed25519 key there, keeps the private key, and uploads only its self-signed public certificate, which the other members pin for the pool's private link. A pool with any null certificate never serves. The record never carries a private key or a prompt. What a schema cannot say, Network and Link check at run time: the members are distinct and the head is one of them; every member has the pool's owner, today, through Bot; every member was seen from the same public address with a private address in the same subnet; the head's direct link probe to each member passes; each machine's certificate is its own; and any change of membership, owner, head or key revokes the pool, so all machines approve a new one. A machine that leaves (removed, signed out, a different owner) drops its link and its key at once.*/export interface BotMachinePool {/**@@@ -6,7 +6,7 @@const crypto = require('crypto');const ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';--const PREFIX = { user: 'usr', guest: 'gst', app: 'app', mod: 'mod', agent: 'agt', node: 'nod', event: 'evt', media: 'med', project: 'prj', confirmation: 'cnf', consent: 'cns', job: 'job', task: 'tsk', watch: 'wch', wait: 'wai', observation: 'wco', check: 'ckr', subscription: 'sub', deploy: 'dpl', domain: 'dom', manifest: 'mfs', release: 'rel', site: 'sit', robot: 'rob', stream: 'std', webhook_delivery: 'whd', share: 'shr', device: 'dev', hook: 'hok', policy: 'pol', audit: 'aud', agent_stream: 'ags', agent_stream_schedule: 'scd', play_request: 'req', sandbox_session: 'sbs', party: 'pty' };++const PREFIX = { user: 'usr', guest: 'gst', app: 'app', mod: 'mod', agent: 'agt', link: 'lnk', event: 'evt', media: 'med', project: 'prj', confirmation: 'cnf', consent: 'cns', job: 'job', task: 'tsk', watch: 'wch', wait: 'wai', observation: 'wco', check: 'ckr', subscription: 'sub', deploy: 'dpl', domain: 'dom', manifest: 'mfs', release: 'rel', site: 'sit', robot: 'rob', stream: 'std', webhook_delivery: 'whd', share: 'shr', device: 'dev', hook: 'hok', policy: 'pol', audit: 'aud', agent_stream: 'ags', agent_stream_schedule: 'scd', play_request: 'req', sandbox_session: 'sbs', party: 'pty' };const SLUG_TYPES = new Set(['service', 'system']);const ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;const SLUG_RE = /^[a-z][a-z0-9-]{1,39}$/;@@@ -50,11 +50,11 @@ function formatSubject(ref) {return `${ref.type}:${ref.id}`;}--/** Service-token `sub` claim: svc:<slug> | app:app_<ULID> | mod:mod_<ULID> | agent:agt_<ULID> | node:nod_<ULID>. */++/** Service-token `sub` claim: svc:<slug> | app:app_<ULID> | mod:mod_<ULID> | agent:agt_<ULID> | link:lnk_<ULID>. */function principalSub(ref) {if (ref.type === 'service' && SLUG_RE.test(ref.id)) return `svc:${ref.id}`;-- if ((ref.type === 'app' || ref.type === 'mod' || ref.type === 'agent' || ref.type === 'node') && isSubjectId(ref.type, ref.id)) return `${ref.type}:${ref.id}`;-- throw new TypeError('principals are service, app, mod, agent or node subjects');++ if ((ref.type === 'app' || ref.type === 'mod' || ref.type === 'agent' || ref.type === 'link') && isSubjectId(ref.type, ref.id)) return `${ref.type}:${ref.id}`;++ throw new TypeError('principals are service, app, mod, agent or link subjects');}/** Transitional Media reference for today's rows (Wave 4 introduces med_ ids). */@@@ -5,7 +5,7 @@"status": "active","visibility": "first-party","sensitive": true,-- "description": "Use a person's own computer with a Network service token naming that person in X-OV-Subject: the computer kinds of bot.command@1 (exec, fs, screen, input, browser) through POST /api/v1/robots/:id/commands, for a machine (openvibe.bot/machines) whose owner is that person and who turned on 'Let my agent use this computer'. Bot takes them from no one else (never an operator or a queue member), and the machine's owner sets on the machine itself what may happen (OpenVibe Link's computer plugin: observe, ask, trusted or full; ask waits for the owner's approval there). The answer is bot.command-result@1's data; every command is audited, a refused one emits bot.command.refused. OpenVibe.Watch (a `node` source) may send only `probe` — one GET or one TCP connect to a private, loopback or link-local address from the machine — and only where the owner turned on \"Let my watches check things from this computer\". The live screen (screen_stream) is not part of it: only the machine's owner starts it, on openvibe.bot, and Bot fills in the machine's private stream.",++ "description": "Use a person's own computer with a Network service token naming that person in X-OV-Subject: the computer kinds of bot.command@1 (exec, fs, screen, input, browser) through POST /api/v1/robots/:id/commands, for a machine (openvibe.link/machines) whose owner is that person and who turned on 'Let my agent use this computer'. Bot takes them from no one else (never an operator or a queue member), and the machine's owner sets on the machine itself what may happen (OpenVibe Link's computer plugin: observe, ask, trusted or full; ask waits for the owner's approval there). The answer is bot.command-result@1's data; every command is audited, a refused one emits bot.command.refused. OpenVibe.Watch (a `node` source) may send only `probe` — one GET or one TCP connect to a private, loopback or link-local address from the machine — and only where the owner turned on \"Let my watches check things from this computer\". The live screen (screen_stream) is not part of it: only the machine's owner starts it, on openvibe.bot, and Bot fills in the machine's private stream.","permissions": ["bot:computer:control"],@@@ -5,7 +5,7 @@"status": "active","visibility": "first-party","sensitive": true,-- "description": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.network/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",++ "description": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.link/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.","permissions": ["bot:machine:read","bot:machine:rename",nnew file mode 100644@@@ -0,0 +1,23 @@++{++ "id": "network.link.manage",++ "version": "1.0",++ "owner": "network",++ "status": "active",++ "visibility": "internal",++ "description": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",++ "permissions": [++ "nodes:pair",++ "nodes:read",++ "nodes:revoke"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "admin",++ "events": ["network.link.revoked"],++ "implementedBy": [++ "POST /internal/link-pairings",++ "GET /internal/link-principals/:id",++ "POST /internal/link-principals/:id/revoke"++ ]++}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "id": "network.link.report",++ "version": "1.0",++ "owner": "network",++ "status": "active",++ "visibility": "internal",++ "description": "Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",++ "permissions": [++ "nodes:write"++ ],++ "resourceConstraints": [++ "none"++ ],++ "quotaClass": "admin",++ "events": [],++ "implementedBy": [++ "POST /internal/links/report",++ "POST /internal/registry/instances/report"++ ],++ "inputSchema": "network.link-report-request@1",++ "outputSchema": "network.link-list-result@1"++}nnew file mode 100644@@@ -0,0 +1,25 @@++{++ "id": "network.link.self.manage",++ "version": "1.0",++ "owner": "network",++ "status": "active",++ "visibility": "internal",++ "description": "A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",++ "permissions": [++ "nodes:self"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "admin",++ "events": [],++ "implementedBy": [++ "PUT /api/v1/link/self/capabilities",++ "POST /api/v1/link/self/credential",++ "POST /api/v1/link/self/pools/announce",++ "GET /api/v1/link/self/pools/:id",++ "POST /api/v1/link/self/pools/:id/approve",++ "POST /api/v1/link/self/pools/:id/status",++ "POST /api/v1/link/self/pools/:id/leave"++ ]++}ddeleted file mode 100644@@@ -1,23 +0,0 @@--{-- "id": "network.node.manage",-- "version": "1.0",-- "owner": "network",-- "status": "active",-- "visibility": "internal",-- "description": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",-- "permissions": [-- "nodes:pair",-- "nodes:read",-- "nodes:revoke"-- ],-- "resourceConstraints": [-- "owner"-- ],-- "quotaClass": "admin",-- "events": ["network.node.revoked"],-- "implementedBy": [-- "POST /internal/node-pairings",-- "GET /internal/node-principals/:id",-- "POST /internal/node-principals/:id/revoke"-- ]--}ddeleted file mode 100644@@@ -1,22 +0,0 @@--{-- "id": "network.node.report",-- "version": "1.0",-- "owner": "network",-- "status": "active",-- "visibility": "internal",-- "description": "Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",-- "permissions": [-- "nodes:write"-- ],-- "resourceConstraints": [-- "none"-- ],-- "quotaClass": "admin",-- "events": [],-- "implementedBy": [-- "POST /internal/nodes/report",-- "POST /internal/registry/instances/report"-- ],-- "inputSchema": "network.node-report-request@1",-- "outputSchema": "network.node-list-result@1"--}ddeleted file mode 100644@@@ -1,25 +0,0 @@--{-- "id": "network.node.self.manage",-- "version": "1.0",-- "owner": "network",-- "status": "active",-- "visibility": "internal",-- "description": "A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",-- "permissions": [-- "nodes:self"-- ],-- "resourceConstraints": [-- "owner"-- ],-- "quotaClass": "admin",-- "events": [],-- "implementedBy": [-- "PUT /api/v1/node/self/capabilities",-- "POST /api/v1/node/self/credential",-- "POST /api/v1/node/self/pools/announce",-- "GET /api/v1/node/self/pools/:id",-- "POST /api/v1/node/self/pools/:id/approve",-- "POST /api/v1/node/self/pools/:id/status",-- "POST /api/v1/node/self/pools/:id/leave"-- ]--}@@@ -5,7 +5,7 @@"status": "planned","visibility": "first-party","sensitive": true,-- "description": "PLANNED for OpenVibe.Website Connect (audience openvibe.website, node principal): a Link opens one share's tunnel with POST /api/v1/shares/:id/tunnel and a website.share-connect-request@1. The edge checks the Network node token (client credentials, subject node:nod_…, audience openvibe.website) and the short-lived, single-use tunnel token minted by Connect; neither alone opens the route. The node token's subject must equal node:<node_id> from the request, and a mismatch is refused and audited. Not delegated to user or app tokens.",++ "description": "PLANNED for OpenVibe.Website Connect (audience openvibe.website, link principal): a Link opens one share's tunnel with POST /api/v1/shares/:id/tunnel and a website.share-connect-request@1. The edge checks the Network Link token (client credentials, subject link:lnk_…, audience openvibe.website) and the short-lived, single-use tunnel token minted by Connect; neither alone opens the route. The Link token's subject must equal link:<node_id> from the request, and a mismatch is refused and audited. Not delegated to user or app tokens.","permissions": ["website:tunnels:connect"],@@@ -5,7 +5,7 @@"status": "planned","visibility": "public","sensitive": true,-- "description": "PLANNED for OpenVibe.Website Connect (audience openvibe.website, owner or project scope): revoke and delete a share with DELETE /api/v1/shares/:id. Connect writes website.share.revoked, closes the edge route and tunnel within its 5-second revocation budget and keeps the audit trail; a public hostname returns 410, not a cached page. Deleting a node in Network closes every share for that node through network.node.revoked.",++ "description": "PLANNED for OpenVibe.Website Connect (audience openvibe.website, owner or project scope): revoke and delete a share with DELETE /api/v1/shares/:id. Connect writes website.share.revoked, closes the edge route and tunnel within its 5-second revocation budget and keeps the audit trail; a public hostname returns 410, not a cached page. Deleting a node in Network closes every share for that node through network.link.revoked.","permissions": ["website:shares:delete"],@@@ -47,7 +47,7 @@},{"question": "Can an agent take full control of my computer?",-- "answer": "Only if you set that Node to Full control on the device itself. It is time-boxed and recorded, you can see when an agent is in control, and a button, a hotkey or one command ends it. Most people use Trusted: routine actions run and risky ones ask first."++ "answer": "Only if you set that Link to Full control on the device itself. It is time-boxed and recorded, you can see when an agent is in control, and a button, a hotkey or one command ends it. Most people use Trusted: routine actions run and risky ones ask first."},{"question": "Can I run Actor myself?",@@@ -234,7 +234,7 @@["fa-robot","Robots too",-- "The same Node drives robots on OpenVibe.Bot, so one install covers your computers and your robots.",++ "The same Link drives robots on OpenVibe.Bot, so one install covers your computers and your robots.","https://openvibe.bot"]]@@@ -257,7 +257,7 @@["fa-server","Devices",-- "Every Node with its status, a terminal, its files and its screen."++ "Every Link with its status, a terminal, its files and its screen."],["fa-robot",@@@ -1,6 +1,6 @@{-- "name": "OpenVibe.Node",-- "repository": "OpenVibers/OpenVibe.Node",++ "name": "OpenVibe.Link",++ "repository": "OpenVibers/OpenVibe.Link","service": null,"product": null,"stage": "alpha",@@@ -32,7 +32,7 @@},{"question": "Can it run on my own machine?",-- "answer": "Yes. With OpenVibe.Run and the OpenVibe node on your computer, a task can run on your own hardware instead of a server."++ "answer": "Yes. With OpenVibe.Run and the OpenVibe Link on your computer, a task can run on your own hardware instead of a server."},{"question": "Where is the code?",@@@ -69,9 +69,9 @@"network.integration.github.read","network.modules.read","network.modules.write",-- "network.node.manage",-- "network.node.report",-- "network.node.self.manage",++ "network.link.manage",++ "network.link.report",++ "network.link.self.manage","network.notifications.push","network.operator.alert","network.project.manage",@@@ -105,7 +105,7 @@"network.grant.changed","network.mod.grants_changed","network.module.updated",-- "network.node.revoked",++ "network.link.revoked","network.notification.created","network.principal_grant.changed","network.subject.merged",@@@ -95,7 +95,7 @@"none": "no claims: one process runs the scheduler; check.run() refuses a second check of a watch in that process"}},-- "notes": "Port 4730, unit openvibe-watch.service. Persistent, user-defined observations and conditions over pages, feeds and APIs (plan T18): the registry, the check engine with the pull carriers (http, feed, api; conditional GET, per-host spacing, backoff, Retry-After), observations and watch.* events through a transactional outbox, the bearer API (/api/v1, loopback) and, since 0.2.0, the public site at openvibe.watch: sign in with OpenVibe (OAuth client watch, PKCE S256, sessions in Watch's database) and create, edit, pause, check and delete your watches in the browser. Event, webhook, Run and Node carriers, AI extraction and actions come in later T18 steps and are not offered. A fired condition becomes a WATCH_TRIGGERED notification through OpenVibe.Network.",++ "notes": "Port 4730, unit openvibe-watch.service. Persistent, user-defined observations and conditions over pages, feeds and APIs (plan T18): the registry, the check engine with the pull carriers (http, feed, api; conditional GET, per-host spacing, backoff, Retry-After), observations and watch.* events through a transactional outbox, the bearer API (/api/v1, loopback) and, since 0.2.0, the public site at openvibe.watch: sign in with OpenVibe (OAuth client watch, PKCE S256, sessions in Watch's database) and create, edit, pause, check and delete your watches in the browser. Event, webhook, Run and Link carriers, AI extraction and actions come in later T18 steps and are not offered. A fired condition becomes a WATCH_TRIGGERED notification through OpenVibe.Network.","health": "/api/health","ready": "/api/ready","internalOrigin": "http://127.0.0.1:4730",@@@ -1,12 +1,12 @@{"name": "openvibe-contracts",-- "version": "0.371.0",++ "version": "0.372.0","lockfileVersion": 3,"requires": true,"packages": {"": {"name": "openvibe-contracts",-- "version": "0.371.0",++ "version": "0.372.0","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.371.0",++ "version": "0.372.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",@@@ -23,7 +23,7 @@* touched package.json's pins (so a bump without a STATUS refresh is caught): checked by the date only.** Every other checkout under the root named OpenVibe.* (a client or repository with no service manifest, such as-- * OpenVibe.Node or OpenVibe.Extensions) needs a STATUS.json with repository, stage, deployed, features and updated, so++ * OpenVibe.Link or OpenVibe.Extensions) needs a STATUS.json with repository, stage, deployed, features and updated, so* the estate does not show it blank; its README is not held to the service headings. One whose STATUS `stage` is* closed, retired or archived (OpenVibe.Realtime, closed by ADR-005) is skipped.*@@@ -438,7 +438,7 @@ function checkRepo(root, manifest, ctx) {}/**-- * A checkout with no service manifest (OpenVibe.Node, OpenVibe.Extensions): it needs a STATUS.json the estate can++ * A checkout with no service manifest (OpenVibe.Link, OpenVibe.Extensions): it needs a STATUS.json the estate can* read, and its facts are checked; the service README headings are not asked of it.*/function checkCheckout(root, name, ctx) {@@@ -15,7 +15,7 @@* package.json lacks is read from a nested one (apps/server, examples/<name>: at most three levels* down, never node_modules), then from STATUS.json's prose* STATUS.json repository, stage, runtime, domain, database, deployed and wave; version when there is no-- * package.json (a Go repository such as OpenVibe.Node)++ * package.json (a Go repository such as OpenVibe.Link)* migrations/ the count and the dialect the SQL declares* this repository's manifests/services|products/*.json the service or product the checkout is*@@@ -188,20 +188,20 @@ try {// A checkout with no service manifest: OpenVibe.* needs a STATUS.json; closed ones and other names are skipped.const manifests = [service('lib', 'OpenVibe.Lib')];-- checkout('OpenVibe.Node', { 'README.md': '# Node\n', 'go.mod': 'module x\n' });++ checkout('OpenVibe.Link', { 'README.md': '# Link\n', 'go.mod': 'module x\n' });checkout('OpenVibe.Realtime', { 'STATUS.json': { repository: 'OpenVibers/OpenVibe.Realtime', stage: 'closed' } });checkout('AFResume', { 'README.md': '# CV\n' });let all = collect(facts, manifests);const byId = (id) => all.find((x) => x.id === id);-- ok(byId('node') && /STATUS\.json missing/.test(gapsOf(byId('node'))), 'an OpenVibe.* checkout without a STATUS.json is a gap');++ ok(byId('link') && /STATUS\.json missing/.test(gapsOf(byId('link'))), `an OpenVibe.* checkout without a STATUS.json is a gap: ${JSON.stringify(byId('link'))}`);ok(byId('realtime') && byId('realtime').skipped === 'closed', 'a closed repository is skipped');ok(!all.some((x) => /afresume/i.test(x.id)), 'a checkout not named OpenVibe.* is not the platform\'s');ok(byId('wiki') && byId('wiki').gaps.some((g) => /STATUS\.json missing/.test(g)), 'every unmanifested OpenVibe.* checkout is held to a STATUS.json');-- checkout('OpenVibe.Node', { 'README.md': '# Node\n', 'STATUS.json': { repository: 'OpenVibers/OpenVibe.Node', stage: 'alpha', version: '0.13.0', deployed: false, features: ['pairing'], updated: '2099-01-01' } });-- all = collect(facts, manifests, new Set(['node']));-- ok(all.length === 1 && all[0].id === 'node' && all[0].gaps.length === 0, `--only reaches a repository checkout, and a complete STATUS is current: ${JSON.stringify(all)}`);-- checkout('OpenVibe.Node', { 'README.md': '# Node\n', 'STATUS.json': { repository: 'OpenVibers/OpenVibe.Node', stage: 'alpha' } });-- all = collect(facts, manifests, new Set(['node']));++ checkout('OpenVibe.Link', { 'README.md': '# Link\n', 'STATUS.json': { repository: 'OpenVibers/OpenVibe.Link', stage: 'alpha', version: '0.13.0', deployed: false, features: ['pairing'], updated: '2099-01-01' } });++ all = collect(facts, manifests, new Set(['link']));++ ok(all.length === 1 && all[0].id === 'link' && all[0].gaps.length === 0, `--only reaches a repository checkout, and a complete STATUS is current: ${JSON.stringify(all)}`);++ checkout('OpenVibe.Link', { 'README.md': '# Link\n', 'STATUS.json': { repository: 'OpenVibers/OpenVibe.Link', stage: 'alpha' } });++ all = collect(facts, manifests, new Set(['link']));ok(all[0].gaps.some((g) => /no "features"/.test(g)) && all[0].gaps.some((g) => /no "deployed"/.test(g)), 'a repository STATUS needs deployed and features');ok(summarize([{ gaps: [] }, { skipped: 'closed', gaps: [] }]) === '1/1 current, 1 closed skipped', 'the summary counts closed repositories apart');nnew file mode 100644@@@ -0,0 +1,10 @@++'use strict';++const assert = require('node:assert/strict');++const { spawnSync } = require('node:child_process');++const path = require('node:path');++const root = path.resolve(__dirname, '..');++const oldName = ['OpenVibe[ .-]No' + 'de', 'openvibe-' + 'node', 'no' + 'd_', 'node[.]openvibe[.]network', 'node:lnk_', 'openvibe[.](bot|my|network)/mach' + 'ines'].join('|');++const result = spawnSync('git', ['grep', '-n', '-I', '-i', '-E', oldName, '--', '.', ':!migrations/**', ':!test/link-brand.test.js'], { cwd: root, encoding: 'utf8' });++assert.ok(result.status === 0 || result.status === 1, result.stderr || 'git grep failed');++assert.equal(result.status, 1, `Old Link brand or principal returned:\n${(result.stdout || '').split('\n').slice(0, 10).join('\n')}`);++console.log(' ok old Link brand and principal IDs are absent');nnew file mode 100644@@@ -0,0 +1,16 @@++'use strict';++const assert = require('node:assert/strict');++const contracts = require('..');++const example = require('../contracts/link/compute.v1.json').examples[0];++++assert.equal(contracts.validate('link.compute@1', example).valid, true);++assert.equal(contracts.validate('link.compute@1', {++ ...example, sources: [...example.sources, { kind: 'exchange', name: 'GPU', status: 'ready', reason: null, price: null }],++}).valid, false, 'a ready rental needs a live price and quote lifetime');++assert.equal(contracts.validate('link.compute@1', {++ ...example, candidates: [{ ...example.candidates[0], price: { amount_usd: -1, unit: 'hour' } }],++}).valid, false, 'negative prices cannot be displayed');++assert.equal(contracts.validate('link.compute@1', {++ ...example, candidates: [{ ...example.candidates[0], status: 'imaginary' }],++}).valid, false, 'unknown placement state is rejected');++console.log('link.compute contract: ok');@@@ -3,7 +3,7 @@ const assert = require('assert');const contracts = require('..');const names = [-- 'usage-sample', 'telemetry-sample', 'node-capabilities', 'service-instance',++ 'usage-sample', 'telemetry-sample', 'link-capabilities', 'service-instance','runtime-offer', 'storage-offer', 'delivery-offer', 'agent-offer', 'harness-offer', 'job',];@@@ -38,7 +38,7 @@ function reading(id, startedMs, n, quantity) {return {id: key, idempotency_key: key, service: 'run', project: record.project, subject: record.subject, resource: id,node: record.node, operation: 'function.invoke', quantity, unit: 's', at: new Date(startedMs + n * 1000).toISOString(),-- source: 'openvibe-node.worker',++ source: 'openvibe-link.worker',};}const fromUsage = (f) => reading(f.id, f.started_ms, f.second, 1);@@@ -36,6 +36,9 @@ function sub(file, ...args) {for (const line of skipsIn(`${r.stdout}${r.stderr}`)) if (!skips.includes(line)) skips.push(line);}++sub(path.join(ROOT, 'test/link-brand.test.js'));++sub(path.join(ROOT, 'test/link-compute.test.js'));++// ── Catalog integrity ────────────────────────────────────────────────────const files = [];(function walk(d) { for (const e of fs.readdirSync(d, { withFileTypes: true })) { const p = path.join(d, e.name); if (e.isDirectory()) walk(p); else if (/\.v\d+\.json$/.test(e.name)) files.push(path.relative(path.join(ROOT, 'contracts'), p)); } })(path.join(ROOT, 'contracts'));@@@ -610,10 +613,10 @@ for (const kind of ['user', 'guest', 'app', 'mod']) {ok(ids.newId('user', 1000) < ids.newId('user', 2000), 'ulids sort by time');ok(ids.parseSubject('user:42') === null && ids.parseSubject('user') === null && ids.parseSubject('svc:live') === null, 'malformed subjects are rejected');ok(ids.principalSub({ type: 'service', id: 'live' }) === 'svc:live', 'service principal sub');--const nodeId = ids.newId('node');--ok(nodeId.startsWith('nod_') && ids.principalSub({ type: 'node', id: nodeId }) === `node:${nodeId}`, 'node principal sub is node:nod_<ULID>');--assert.throws(() => ids.principalSub({ type: 'node', id: 'nod_42' }), /principals/);--ok(!ids.SUBJECT_TYPES.includes('node') && ids.parseSubject(`node:${nodeId}`) === null, 'a node is a principal, never a subject');++const nodeId = ids.newId('link');++ok(nodeId.startsWith('lnk_') && ids.principalSub({ type: 'link', id: nodeId }) === `link:${nodeId}`, 'node principal sub is link:lnk_<ULID>');++assert.throws(() => ids.principalSub({ type: 'link', id: 'lnk_42' }), /principals/);++ok(!ids.SUBJECT_TYPES.includes('node') && ids.parseSubject(`link:${nodeId}`) === null, 'a node is a principal, never a subject');assert.throws(() => ids.principalSub({ type: 'user', id: ids.newId('user') }), /principals/);for (const [kind, prefix] of [['watch', 'wch'], ['observation', 'wco'], ['check', 'ckr'], ['job', 'job'], ['task', 'tsk']]) ok(ids.newId(kind).startsWith(`${prefix}_`), `newId(${kind}) is ${prefix}_<ULID>`);ok(contracts.validate('media.media-ref', { media_id: ids.legacyMediaId('live', 'vod', 42) }).valid, 'legacyMediaId is a valid MediaRef');@@@ -678,9 +681,9 @@ ok(V(sandboxTok, { acceptSandbox: true }).ok, 'sandbox app token accepted when tok(V(serviceAuth.signServiceToken({ ...appClaims, env: 'production' }, kp.privateKey)).ok, 'production app token accepted');ok(V(serviceAuth.signServiceToken((({ project_id, ...rest }) => rest)(appClaims), kp.privateKey)).code === 'token.invalid_claims', 'an app token needs project_id');ok(contracts.ids.newId('project').startsWith('prj_'), 'project ids use the prj prefix');--const nodeClaims = { ...claims, sub: 'node:nod_01J0000000000000000000000Z', actor_type: 'node' };++const nodeClaims = { ...claims, sub: 'link:lnk_01J0000000000000000000000Z', actor_type: 'link' };ok(V(serviceAuth.signServiceToken(nodeClaims, kp.privateKey)).ok, 'node token accepted');--ok(V(serviceAuth.signServiceToken({ ...nodeClaims, sub: 'node:nod_42' }, kp.privateKey)).code === 'token.invalid_claims', 'a malformed node id is rejected');++ok(V(serviceAuth.signServiceToken({ ...nodeClaims, sub: 'link:lnk_42' }, kp.privateKey)).code === 'token.invalid_claims', 'a malformed node id is rejected');const agentId = contracts.ids.newId('agent');const agentClaims = { ...claims, sub: contracts.ids.principalSub({ type: 'agent', id: agentId }), actor_type: 'agent', cap: ['chat.presence.read'], cap_confirm: ['chat.message.send'], project_id: 'prj_01J0000000000000000000000Z', env: 'production', on_behalf_of: 'usr_01J0000000000000000000000Z', act: { sub: 'svc:actor' } };const agentTok = V(serviceAuth.signServiceToken(agentClaims, kp.privateKey));
@@ -1,5 +1,17 @@
@@ -1,5 +1,17 @@
# Changelog
# Changelog
+## 0.372.0 — 2026-11-09
+
+- The machine product Node became OpenVibe Link. The network registry contracts are `network.link@1`,
+ `network.link-report-request@1` and `network.link-list-result@1`, the event is `network.link.revoked`, and the platform
+ carries `platform.link-capabilities@1` and `link.compute@1`. The capabilities `network.node.manage`,
+ `network.node.report` and `network.node.self.manage` are `network.link.manage`, `network.link.report` and
+ `network.link.self.manage`; their internal routes and the manifests follow.
+- A paired machine is a `link` principal: identity.service-token-claims actor_type `node` is `link` with sub `link:lnk_…`,
+ and `ids.principalSub` mints `link:lnk_…` (the old prefix and ids are gone, no alias). The AI provider `node` is `link`
+ in ai.credential-put@1/@2 and ai.credential@1/@2. The old contract ids are deprecated with their Link replacements; the
+ schema corrections are recorded against the current release and the ones before it.
+
## 0.371.0 — 2026-11-09
## 0.371.0 — 2026-11-09
- Exchange API platform and search: exchange.key@1, exchange.key-created@1, exchange.usage@1, exchange.deployments@1, exchange.search-result@1, exchange.image-result@1, and the Exchange manifest's routes
- Exchange API platform and search: exchange.key@1, exchange.key-created@1, exchange.usage@1, exchange.deployments@1, exchange.search-result@1, exchange.image-result@1, and the Exchange manifest's routes
@@ -851,7 +863,7 @@
@@ -851,7 +863,7 @@
## 0.261.0 — 2026-10-31
## 0.261.0 — 2026-10-31
- **The machines hub** (plan L34, steps M2 and M4). A person's computers are the network's: Bot stays the device
- **The machines hub** (plan L34, steps M2 and M4). A person's computers are the network's: Bot stays the device
- registry underneath, and Network fronts it on openvibe.network/machines for the person and for apps the person
+ registry underneath, and Network fronts it on openvibe.link/machines for the person and for apps the person
consented to.
consented to.
- **`network.machine@1`:** one machine as the hub shows it — its id (Bot's `dev_…`), name, kind, os, whether it is
- **`network.machine@1`:** one machine as the hub shows it — its id (Bot's `dev_…`), name, kind, os, whether it is
online, when it was last seen, its `roles` and what it does right now (`running`: its Codes jobs, its desks, its
online, when it was last seen, its `roles` and what it does right now (`running`: its Codes jobs, its desks, its
@@ -3119,7 +3131,7 @@ contract changes.
@@ -3119,7 +3131,7 @@ contract changes.
**The node-revoked event contract** (plan T2 follow-up; the deferred item in OpenVibe.Network's
**The node-revoked event contract** (plan T2 follow-up; the deferred item in OpenVibe.Network's
`docs/t2-cells-and-node-principal.md` section 10, "Revocation latency outside Bot"). Adds
`docs/t2-cells-and-node-principal.md` section 10, "Revocation latency outside Bot"). Adds
-`network.node.revoked@1` (owner `network`, first-party, active): a node principal (`nod_…`), a person's paired
+`network.link.revoked@1` (owner `network`, first-party, active): a node principal (`lnk_…`), a person's paired
machine, was revoked through `POST /internal/node-principals/:id/revoke` (`network.node.manage`, the service that
machine, was revoked through `POST /internal/node-principals/:id/revoke` (`network.node.manage`, the service that
paired it) or `POST /api/v1/me/nodes/:id/revoke` (the owner's session), or with the owner's account. The payload is
paired it) or `POST /api/v1/me/nodes/:id/revoke` (the owner's session), or with the owner's account. The payload is
`{ node_id, principal_id, owner, reason?, at }` (`extra` fields refused): `owner` is the person, project or platform
`{ node_id, principal_id, owner, reason?, at }` (`extra` fields refused): `owner` is the person, project or platform
@@ -3523,9 +3535,9 @@ Network grants for the usage producers follow this release. Additive.
@@ -3523,9 +3535,9 @@ Network grants for the usage producers follow this release. Additive.
**Node principals, node capabilities and per-kind resource offers** (plan T1 lane A, the contracts brief of
**Node principals, node capabilities and per-kind resource offers** (plan T1 lane A, the contracts brief of
OpenVibe.Network `docs/t2-cells-and-node-principal.md` §9.1):
OpenVibe.Network `docs/t2-cells-and-node-principal.md` §9.1):
-- `lib/ids.js`: `PREFIX.node = 'nod'`; `principalSub({ type: 'node', id })` gives `node:nod_<ULID>`. A node is a
+- `lib/ids.js`: `PREFIX.node = 'nod'`; `principalSub({ type: 'link', id })` gives `link:lnk_<ULID>`. A node is a
principal only: `node` is not a `SUBJECT_TYPES` entry nor an `identity.subject-ref@1` type.
principal only: `node` is not a `SUBJECT_TYPES` entry nor an `identity.subject-ref@1` type.
-- `identity.service-token-claims`: `sub` also accepts `node:nod_<ULID>` and `actor_type` gains `node`, so
+- `identity.service-token-claims`: `sub` also accepts `link:lnk_<ULID>` and `actor_type` gains `link`, so
`verifyServiceToken` accepts a node token and still rejects a malformed node id. Fixtures `node-token` (valid) and
`verifyServiceToken` accepts a node token and still rejects a malformed node id. Fixtures `node-token` (valid) and
`node-bad-id` (invalid).
`node-bad-id` (invalid).
- New capabilities `network.node.manage` (a service mints pairing codes and reads or revokes the node principals it
- New capabilities `network.node.manage` (a service mints pairing codes and reads or revokes the node principals it
@@ -242,7 +242,7 @@ A job moves `queued → placed → running` and ends `succeeded`, `failed`, `can
@@ -242,7 +242,7 @@ A job moves `queued → placed → running` and ends `succeeded`, `failed`, `can
`run.job.queued|started|succeeded|failed|cancelled|expired`. Run mints the job id (`job_<ULID>`, `ids.newId('job')`)
`run.job.queued|started|succeeded|failed|cancelled|expired`. Run mints the job id (`job_<ULID>`, `ids.newId('job')`)
and passes it unchanged as the `platform.job@1` id, so every metering reading is `run:<job id>:<n>` (service `run`,
and passes it unchanged as the `platform.job@1` id, so every metering reading is `run:<job id>:<n>` (service `run`,
operation `function.invoke`, unit `s`) and `usage.seconds` is their sum. Inputs are Media objects pinned by sha256
operation `function.invoke`, unit `s`) and `usage.seconds` is their sum. Inputs are Media objects pinned by sha256
-(never a URL); `egress` takes Node's `worker.egress` values (`none` by default, `public`, `openvibe-only`).
+(never a URL); `egress` takes Link's `worker.egress` values (`none` by default, `public`, `openvibe-only`).
## Versioning and compatibility
## Versioning and compatibility
@@ -2,14 +2,14 @@
@@ -2,14 +2,14 @@
"_about": "Active capabilities still without an input or output schema (roadmap WS-C task 4). test/capability-schemas.test.js fails on any active capability missing a schema that is not listed here, and on a listed one that has since gained it (remove it from this list). The list only shrinks: fill a capability's schemas, then delete its row. A request with no body names common.no-body@1; a response of bytes names common.binary@1.",
"_about": "Active capabilities still without an input or output schema (roadmap WS-C task 4). test/capability-schemas.test.js fails on any active capability missing a schema that is not listed here, and on a listed one that has since gained it (remove it from this list). The list only shrinks: fill a capability's schemas, then delete its row. A request with no body names common.no-body@1; a response of bytes names common.binary@1.",
"gaps": [
"gaps": [
{
{
- "id": "network.node.manage",
+ "id": "network.link.manage",
"missing": [
"missing": [
"inputSchema",
"inputSchema",
"outputSchema"
"outputSchema"
]
]
},
},
{
{
- "id": "network.node.self.manage",
+ "id": "network.link.self.manage",
"missing": [
"missing": [
"inputSchema",
"inputSchema",
"outputSchema"
"outputSchema"
@@ -18,6 +18,78 @@
@@ -18,6 +18,78 @@
"base": "v0.289.0",
"base": "v0.289.0",
"fixedIn": "0.289.1",
"fixedIn": "0.289.1",
"reason": "0.289.0 made `project_id` optional on billing.credit-hold-create-request@1 for a personal hold, but billing.credit-hold@1 still required a string, so the answer Billing's presentHold sends for a personal hold (`project_id: null`) did not validate. The string-only rule never described a personal hold's answer; project holds still send a string, and `project_id` widens to optional [string, null]."
"reason": "0.289.0 made `project_id` optional on billing.credit-hold-create-request@1 for a personal hold, but billing.credit-hold@1 still required a string, so the answer Billing's presentHold sends for a personal hold (`project_id: null`) did not validate. The string-only rule never described a personal hold's answer; project holds still send a string, and `project_id` widens to optional [string, null]."
+ },
+ {
+ "id": "ai.credential-put",
+ "base": "v0.352.0",
+ "fixedIn": "0.372.0",
+ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential-put@1 and @2 is the same provider under its new name. Only our own AI service ever produced it, and it moves to `link` in the same release, so the enum is corrected in place rather than carried as an alias."
+ },
+ {
+ "id": "ai.credential",
+ "base": "v0.352.0",
+ "fixedIn": "0.372.0",
+ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential@1 and @2 is the same provider under its new name, produced and read only by our own AI service, which moves to `link` in the same release."
+ },
+ {
+ "id": "identity.service-token-claims",
+ "base": "v0.352.0",
+ "fixedIn": "0.372.0",
+ "reason": "The machine product Node became OpenVibe Link: a paired machine's principal actor_type is `link` (sub `link:lnk_…`) instead of `node`. Network mints these tokens and every receiver is ours, updated in the same release; no third party consumed the old value."
+ },
+ {
+ "id": "ai.credential-put",
+ "base": "v0.365.0",
+ "fixedIn": "0.372.0",
+ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential-put@1 and @2 is the same provider under its new name. Only our own AI service ever produced it, and it moves to `link` in the same release, so the enum is corrected in place rather than carried as an alias."
+ },
+ {
+ "id": "ai.credential",
+ "base": "v0.365.0",
+ "fixedIn": "0.372.0",
+ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential@1 and @2 is the same provider under its new name, produced and read only by our own AI service, which moves to `link` in the same release."
+ },
+ {
+ "id": "identity.service-token-claims",
+ "base": "v0.365.0",
+ "fixedIn": "0.372.0",
+ "reason": "The machine product Node became OpenVibe Link: a paired machine's principal actor_type is `link` (sub `link:lnk_…`) instead of `node`. Network mints these tokens and every receiver is ours, updated in the same release; no third party consumed the old value."
+ },
+ {
+ "id": "ai.credential-put",
+ "base": "v0.368.0",
+ "fixedIn": "0.372.0",
+ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential-put@1 and @2 is the same provider under its new name. Only our own AI service ever produced it, and it moves to `link` in the same release, so the enum is corrected in place rather than carried as an alias."
+ },
+ {
+ "id": "ai.credential",
+ "base": "v0.368.0",
+ "fixedIn": "0.372.0",
+ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential@1 and @2 is the same provider under its new name, produced and read only by our own AI service, which moves to `link` in the same release."
+ },
+ {
+ "id": "identity.service-token-claims",
+ "base": "v0.368.0",
+ "fixedIn": "0.372.0",
+ "reason": "The machine product Node became OpenVibe Link: a paired machine's principal actor_type is `link` (sub `link:lnk_…`) instead of `node`. Network mints these tokens and every receiver is ours, updated in the same release; no third party consumed the old value."
+ },
+ {
+ "id": "ai.credential-put",
+ "base": "v0.371.0",
+ "fixedIn": "0.372.0",
+ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential-put@1 and @2 is the same provider under its new name. Only our own AI service ever produced it, and it moves to `link` in the same release, so the enum is corrected in place rather than carried as an alias."
+ },
+ {
+ "id": "ai.credential",
+ "base": "v0.371.0",
+ "fixedIn": "0.372.0",
+ "reason": "The machine product Node became OpenVibe Link, and the person's own-machine provider is `link`: the `node` value of ai.credential@1 and @2 is the same provider under its new name, produced and read only by our own AI service, which moves to `link` in the same release."
+ },
+ {
+ "id": "identity.service-token-claims",
+ "base": "v0.371.0",
+ "fixedIn": "0.372.0",
+ "reason": "The machine product Node became OpenVibe Link: a paired machine's principal actor_type is `link` (sub `link:lnk_…`) instead of `node`. Network mints these tokens and every receiver is ours, updated in the same release; no third party consumed the old value."
}
}
]
]
}
}
@@ -231,6 +231,41 @@
@@ -231,6 +231,41 @@
"since": "0.263.0",
"since": "0.263.0",
"removeAfter": "1.0.0",
"removeAfter": "1.0.0",
"reason": "Report's personal AI usage answers rows, totals, by_paid_by, insights and source (plan L41 R1); v1's usage array and its field names never matched the route, so the shape moved to a new major (2026-10-31)"
"reason": "Report's personal AI usage answers rows, totals, by_paid_by, insights and source (plan L41 R1); v1's usage array and its field names never matched the route, so the shape moved to a new major (2026-10-31)"
+ },
+ {
+ "id": "network.node",
+ "replacement": "network.link@1",
+ "since": "0.372.0",
+ "removeAfter": "1.0.0",
+ "reason": "The machine product Node became OpenVibe Link: network.node@1 became network.link@1 in the rename (2026-11-09)"
+ },
+ {
+ "id": "network.node-report-request",
+ "replacement": "network.link-report-request@1",
+ "since": "0.372.0",
+ "removeAfter": "1.0.0",
+ "reason": "The machine product Node became OpenVibe Link: network.node-report-request@1 became network.link-report-request@1 in the rename (2026-11-09)"
+ },
+ {
+ "id": "network.node-list-result",
+ "replacement": "network.link-list-result@1",
+ "since": "0.372.0",
+ "removeAfter": "1.0.0",
+ "reason": "The machine product Node became OpenVibe Link: network.node-list-result@1 became network.link-list-result@1 in the rename (2026-11-09)"
+ },
+ {
+ "id": "platform.node-capabilities",
+ "replacement": "platform.link-capabilities@1",
+ "since": "0.372.0",
+ "removeAfter": "1.0.0",
+ "reason": "The machine product Node became OpenVibe Link: platform.node-capabilities@1 became platform.link-capabilities@1, and link.compute@1 carries the Link's offer in the platform fabric (2026-11-09)"
+ },
+ {
+ "id": "network.node.revoked",
+ "replacement": "network.link.revoked@1",
+ "since": "0.372.0",
+ "removeAfter": "1.0.0",
+ "reason": "The machine product Node became OpenVibe Link: the network.node.revoked event became network.link.revoked and its principal ids move to lnk_ (2026-11-09)"
}
}
]
]
}
}
@@ -13,14 +13,14 @@
@@ -13,14 +13,14 @@
"enum": [
"enum": [
"openai",
"openai",
"anthropic",
"anthropic",
- "node"
+ "link"
],
],
"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."
"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."
},
},
"machine": {
"machine": {
"type": "string",
"type": "string",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
+ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
},
},
"base_url": {
"base_url": {
"type": "string",
"type": "string",
@@ -78,7 +78,7 @@
@@ -78,7 +78,7 @@
"if": {
"if": {
"properties": {
"properties": {
"provider": {
"provider": {
- "const": "node"
+ "const": "link"
}
}
},
},
"required": [
"required": [
@@ -17,7 +17,7 @@
@@ -17,7 +17,7 @@
"minimax",
"minimax",
"openrouter",
"openrouter",
"custom",
"custom",
- "node"
+ "link"
],
],
"description": "anthropic = Anthropic's Messages API; openai = any OpenAI-compatible endpoint; deepseek, minimax and openrouter = those providers; custom = the endpoint in base_url (a URL that speaks the OpenAI or the Anthropic format, by the route it is used on); node = the person's own machine (no key, no base_url, no cost)."
"description": "anthropic = Anthropic's Messages API; openai = any OpenAI-compatible endpoint; deepseek, minimax and openrouter = those providers; custom = the endpoint in base_url (a URL that speaks the OpenAI or the Anthropic format, by the route it is used on); node = the person's own machine (no key, no base_url, no cost)."
},
},
@@ -42,7 +42,7 @@
@@ -42,7 +42,7 @@
"machine": {
"machine": {
"type": "string",
"type": "string",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model, or `any` of their own."
+ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model, or `any` of their own."
},
},
"models": {
"models": {
"type": "object",
"type": "object",
@@ -89,7 +89,7 @@
@@ -89,7 +89,7 @@
"if": {
"if": {
"properties": {
"properties": {
"provider": {
"provider": {
- "const": "node"
+ "const": "link"
}
}
},
},
"required": [
"required": [
@@ -26,7 +26,7 @@
@@ -26,7 +26,7 @@
"enum": [
"enum": [
"openai",
"openai",
"anthropic",
"anthropic",
- "node"
+ "link"
]
]
},
},
"machine": {
"machine": {
@@ -35,7 +35,7 @@
@@ -35,7 +35,7 @@
"null"
"null"
],
],
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
- "description": "provider node: the machine it runs on, or `any` of the person's own (contracts 0.221.0)"
+ "description": "provider link: the machine it runs on, or `any` of the person's own (contracts 0.221.0)"
},
},
"base_url": {
"base_url": {
"type": [
"type": [
@@ -30,7 +30,7 @@
@@ -30,7 +30,7 @@
"minimax",
"minimax",
"openrouter",
"openrouter",
"custom",
"custom",
- "node"
+ "link"
]
]
},
},
"machine": {
"machine": {
@@ -39,7 +39,7 @@
@@ -39,7 +39,7 @@
"null"
"null"
],
],
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
- "description": "provider node: the machine it runs on, or `any` of the person's own"
+ "description": "provider link: the machine it runs on, or `any` of the person's own"
},
},
"base_url": {
"base_url": {
"type": [
"type": [
@@ -118,7 +118,7 @@
@@ -118,7 +118,7 @@
"properties": {
"properties": {
"provider": {
"provider": {
"not": {
"not": {
- "const": "node"
+ "const": "link"
}
}
}
}
},
},
@@ -39,7 +39,7 @@
@@ -39,7 +39,7 @@
},
},
"operator": {
"operator": {
"type": "string",
"type": "string",
- "description": "Operator slug or `owner:<subject_id>` for a private node."
+ "description": "Operator slug or `owner:<subject_id>` for a private machine."
},
},
"host": {
"host": {
"type": [
"type": [
@@ -158,7 +158,7 @@
@@ -158,7 +158,7 @@
"node",
"node",
"submitted"
"submitted"
],
],
- "description": "codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it."
+ "description": "codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it."
},
},
"id": {
"id": {
"type": "string",
"type": "string",
@@ -158,7 +158,7 @@
@@ -158,7 +158,7 @@
"node",
"node",
"submitted"
"submitted"
],
],
- "description": "codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it."
+ "description": "codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it."
},
},
"id": {
"id": {
"type": "string",
"type": "string",
@@ -79,7 +79,7 @@
@@ -79,7 +79,7 @@
"null"
"null"
],
],
"maxLength": 128,
"maxLength": 128,
- "description": "The offer's provider, as Run sent it; null for a first-party node."
+ "description": "The offer's provider, as Run sent it; null for a first-party machine."
},
},
"cancel_requested": {
"cancel_requested": {
"type": "boolean",
"type": "boolean",
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/bot/machine-pool.v1.json",
"$id": "https://openvibe.network/contracts/bot/machine-pool.v1.json",
"title": "BotMachinePool",
"title": "BotMachinePool",
- "description": "bot.machine-pool@1 (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC, `rpc-server` on the members and `llama-server --rpc` on the head. OpenVibe.Network holds the record: the owner proposes it on /machines (POST /api/v1/machine-pools, two to eight of their own machines and an optional head; the default head has the most free VRAM), and each machine reads it with its own node token (GET /api/v1/node/self/pools/:id, network.node.self.manage) and approves it locally (POST /api/v1/node/self/pools/:id/approve with { certificate }). A machine's `certificate` is null until it approves on that machine: the Link generates an Ed25519 key there, keeps the private key, and uploads only its self-signed public certificate, which the other members pin for the pool's private link. A pool with any null certificate never serves. The record never carries a private key or a prompt. What a schema cannot say, Network and Node check at run time: the members are distinct and the head is one of them; every member has the pool's owner, today, through Bot; every member was seen from the same public address with a private address in the same subnet; the head's direct link probe to each member passes; each machine's certificate is its own; and any change of membership, owner, head or key revokes the pool, so all machines approve a new one. A machine that leaves (removed, signed out, a different owner) drops its link and its key at once.",
+ "description": "bot.machine-pool@1 (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC, `rpc-server` on the members and `llama-server --rpc` on the head. OpenVibe.Network holds the record: the owner proposes it on /machines (POST /api/v1/machine-pools, two to eight of their own machines and an optional head; the default head has the most free VRAM), and each machine reads it with its own Link token (GET /api/v1/link/self/pools/:id, network.link.self.manage) and approves it locally (POST /api/v1/link/self/pools/:id/approve with { certificate }). A machine's `certificate` is null until it approves on that machine: the Link generates an Ed25519 key there, keeps the private key, and uploads only its self-signed public certificate, which the other members pin for the pool's private link. A pool with any null certificate never serves. The record never carries a private key or a prompt. What a schema cannot say, Network and Link check at run time: the members are distinct and the head is one of them; every member has the pool's owner, today, through Bot; every member was seen from the same public address with a private address in the same subnet; the head's direct link probe to each member passes; each machine's certificate is its own; and any change of membership, owner, head or key revokes the pool, so all machines approve a new one. A machine that leaves (removed, signed out, a different owner) drops its link and its key at once.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
"required": [
"required": [
@@ -6436,30 +6436,30 @@
@@ -6436,30 +6436,30 @@
"status": "retired"
"status": "retired"
},
},
{
{
- "id": "network.node",
+ "id": "network.link",
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
- "schema": "network/node.v1.json",
+ "schema": "network/link.v1.json",
"visibility": "public",
"visibility": "public",
"compatibility": "backward",
"compatibility": "backward",
"status": "active",
"status": "active",
"adr": "ADR-034"
"adr": "ADR-034"
},
},
{
{
- "id": "network.node-report-request",
+ "id": "network.link-report-request",
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
- "schema": "network/node-report-request.v1.json",
+ "schema": "network/link-report-request.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
"status": "active",
"status": "active",
"adr": "ADR-034"
"adr": "ADR-034"
},
},
{
{
- "id": "network.node-list-result",
+ "id": "network.link-list-result",
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
- "schema": "network/node-list-result.v1.json",
+ "schema": "network/link-list-result.v1.json",
"visibility": "public",
"visibility": "public",
"compatibility": "backward",
"compatibility": "backward",
"status": "active",
"status": "active",
@@ -6728,10 +6728,19 @@
@@ -6728,10 +6728,19 @@
"adr": "ADR-034"
"adr": "ADR-034"
},
},
{
{
- "id": "platform.node-capabilities",
+ "id": "link.compute",
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
- "schema": "platform/node-capabilities.v1.json",
+ "schema": "link/compute.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "platform.link-capabilities",
+ "version": "1.0.0",
+ "owner": "network",
+ "schema": "platform/link-capabilities.v1.json",
"visibility": "public",
"visibility": "public",
"compatibility": "backward",
"compatibility": "backward",
"status": "active",
"status": "active",
@@ -7391,10 +7400,10 @@
@@ -7391,10 +7400,10 @@
"adr": "ADR-048"
"adr": "ADR-048"
},
},
{
{
- "id": "network.node.revoked",
+ "id": "network.link.revoked",
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
- "schema": "events/payloads/network.node.revoked.v1.json",
+ "schema": "events/payloads/network.link.revoked.v1.json",
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
"status": "active",
"status": "active",
@@ -83,7 +83,7 @@
@@ -83,7 +83,7 @@
"string",
"string",
"null"
"null"
],
],
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
"description": "The Network node principal the device presents; null for an agent or server that joins through a pre-auth key."
"description": "The Network node principal the device presents; null for an agent or server that joins through a pre-auth key."
},
},
"name": {
"name": {
new file mode 100644
new file mode 100644
@@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/network.link.revoked.v1.json",
+ "title": "NetworkLinkRevokedPayload",
+ "description": "network.link.revoked v1 (OpenVibe.Network; plan T2, docs/t2-cells-and-node-principal.md section 10, migrations/0014_node_pairing.sql). A node principal (lnk_…), a person's paired machine, was revoked: through POST /internal/link-principals/:id/revoke (network.link.manage, the service that paired it), through POST /api/v1/me/nodes/:id/revoke (the owner's session), or with the owner's account. Network revokes the row and clears the credential and its grace window; a machine holding a live node token is refused at its next use (≤ 300 s), and until this event only learned of the revoke at its next reauth (≤ 330 s). A consumer stops the machine at once; a second revoke changes nothing and emits nothing. The credential and its hash are never carried. Envelope: subject { type: node_principal, id: <lnk_…> }, visibility internal, actor the service (svc:<service>) or the owner ({ type: user }) that revoked.",
+ "type": "object",
+ "required": [
+ "node_id",
+ "principal_id",
+ "owner",
+ "at"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "node_id": {
+ "type": "string",
+ "pattern": "^n-[a-z0-9]{26}$",
+ "description": "The machine's node id (platform_node_principals.node_id), minted with the principal and unique among principals."
+ },
+ "principal_id": {
+ "type": "string",
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The revoked node principal. Also the envelope subject id."
+ },
+ "owner": {
+ "description": "Who the machine belongs to, as GET /api/v1/me/nodes and the operator list show it: the person who paired it, the project it is paired into, or the platform for a Host-reported machine.",
+ "oneOf": [
+ {
+ "type": "object",
+ "required": ["kind", "subject"],
+ "additionalProperties": false,
+ "properties": {
+ "kind": { "const": "user" },
+ "subject": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" }
+ }
+ },
+ {
+ "type": "object",
+ "required": ["kind", "project_id"],
+ "additionalProperties": false,
+ "properties": {
+ "kind": { "const": "project" },
+ "project_id": { "type": "string", "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$" }
+ }
+ },
+ {
+ "type": "object",
+ "required": ["kind"],
+ "additionalProperties": false,
+ "properties": {
+ "kind": { "const": "platform" }
+ }
+ }
+ ]
+ },
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Why it was revoked, when the caller gave one (the owner's text or the service's); absent otherwise."
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the revoke was applied (platform_node_principals.revoked_at)."
+ }
+ }
+}
@@ -8,7 +8,7 @@
@@ -8,7 +8,7 @@
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
"machine_id": { "type": "string", "pattern": "^dev_[0-9A-HJKMNP-TV-Z]{26}$" },
"machine_id": { "type": "string", "pattern": "^dev_[0-9A-HJKMNP-TV-Z]{26}$" },
- "principal_id": { "type": "string", "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's node principal on Network, when it has one." },
+ "principal_id": { "type": "string", "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's node principal on Network, when it has one." },
"owner": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The person the machine belongs to." },
"owner": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The person the machine belongs to." },
"at": { "type": "string", "format": "date-time" }
"at": { "type": "string", "format": "date-time" }
}
}
@@ -8,7 +8,7 @@
@@ -8,7 +8,7 @@
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
"machine_id": { "type": "string", "pattern": "^dev_[0-9A-HJKMNP-TV-Z]{26}$" },
"machine_id": { "type": "string", "pattern": "^dev_[0-9A-HJKMNP-TV-Z]{26}$" },
- "principal_id": { "type": "string", "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's node principal on Network, when it has one." },
+ "principal_id": { "type": "string", "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's node principal on Network, when it has one." },
"owner": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The person the machine belongs to." },
"owner": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The person the machine belongs to." },
"at": { "type": "string", "format": "date-time" }
"at": { "type": "string", "format": "date-time" }
}
}
@@ -2,14 +2,14 @@
@@ -2,14 +2,14 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/events/payloads/network.machine.paired.v1.json",
"$id": "https://openvibe.network/contracts/events/payloads/network.machine.paired.v1.json",
"title": "NetworkMachinePairedPayload",
"title": "NetworkMachinePairedPayload",
- "description": "network.machine.paired v1 (OpenVibe.Network, plan L34: the machines hub). A machine was paired with a person's account: it redeemed the one-time code and became theirs. It holds a credential from then on; the code is spent. The machine's device id in Bot is absent until the machine checks in for the first time. Envelope: subject { type: machine, id: <nod_…> }, visibility internal, actor service:network.",
+ "description": "network.machine.paired v1 (OpenVibe.Network, plan L34: the machines hub). A machine was paired with a person's account: it redeemed the one-time code and became theirs. It holds a credential from then on; the code is spent. The machine's device id in Bot is absent until the machine checks in for the first time. Envelope: subject { type: machine, id: <lnk_…> }, visibility internal, actor service:network.",
"type": "object",
"type": "object",
"required": ["principal_id", "owner", "at"],
"required": ["principal_id", "owner", "at"],
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
"machine_id": { "type": "string", "pattern": "^dev_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's device id in Bot's registry, when Bot already knows the device." },
"machine_id": { "type": "string", "pattern": "^dev_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's device id in Bot's registry, when Bot already knows the device." },
"node_id": { "type": "string", "pattern": "^n-[a-z0-9]{26}$", "description": "The machine's node id (platform_node_principals.node_id)." },
"node_id": { "type": "string", "pattern": "^n-[a-z0-9]{26}$", "description": "The machine's node id (platform_node_principals.node_id)." },
- "principal_id": { "type": "string", "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The node principal the pairing created. Also the envelope subject id." },
+ "principal_id": { "type": "string", "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The node principal the pairing created. Also the envelope subject id." },
"owner": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The person the machine belongs to." },
"owner": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The person the machine belongs to." },
"at": { "type": "string", "format": "date-time" }
"at": { "type": "string", "format": "date-time" }
}
}
@@ -8,7 +8,7 @@
@@ -8,7 +8,7 @@
"additionalProperties": false,
"additionalProperties": false,
"properties": {
"properties": {
"machine_id": { "type": "string", "pattern": "^dev_[0-9A-HJKMNP-TV-Z]{26}$" },
"machine_id": { "type": "string", "pattern": "^dev_[0-9A-HJKMNP-TV-Z]{26}$" },
- "principal_id": { "type": "string", "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's node principal on Network, when it had one." },
+ "principal_id": { "type": "string", "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The machine's node principal on Network, when it had one." },
"owner": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The person the machine belonged to." },
"owner": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$", "description": "The person the machine belonged to." },
"reason": { "type": "string", "minLength": 1, "maxLength": 500, "description": "Why it was revoked, when the caller gave one. Absent otherwise." },
"reason": { "type": "string", "minLength": 1, "maxLength": 500, "description": "Why it was revoked, when the caller gave one. Absent otherwise." },
"at": { "type": "string", "format": "date-time" }
"at": { "type": "string", "format": "date-time" }
deleted file mode 100644
deleted file mode 100644
@@ -1,68 +0,0 @@
@@ -1,68 +0,0 @@
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/network.node.revoked.v1.json",
- "title": "NetworkNodeRevokedPayload",
- "description": "network.node.revoked v1 (OpenVibe.Network; plan T2, docs/t2-cells-and-node-principal.md section 10, migrations/0014_node_pairing.sql). A node principal (nod_…), a person's paired machine, was revoked: through POST /internal/node-principals/:id/revoke (network.node.manage, the service that paired it), through POST /api/v1/me/nodes/:id/revoke (the owner's session), or with the owner's account. Network revokes the row and clears the credential and its grace window; a machine holding a live node token is refused at its next use (≤ 300 s), and until this event only learned of the revoke at its next reauth (≤ 330 s). A consumer stops the machine at once; a second revoke changes nothing and emits nothing. The credential and its hash are never carried. Envelope: subject { type: node_principal, id: <nod_…> }, visibility internal, actor the service (svc:<service>) or the owner ({ type: user }) that revoked.",
- "type": "object",
- "required": [
- "node_id",
- "principal_id",
- "owner",
- "at"
- ],
- "additionalProperties": false,
- "properties": {
- "node_id": {
- "type": "string",
- "pattern": "^n-[a-z0-9]{26}$",
- "description": "The machine's node id (platform_node_principals.node_id), minted with the principal and unique among principals."
- },
- "principal_id": {
- "type": "string",
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "The revoked node principal. Also the envelope subject id."
- },
- "owner": {
- "description": "Who the machine belongs to, as GET /api/v1/me/nodes and the operator list show it: the person who paired it, the project it is paired into, or the platform for a Host-reported machine.",
- "oneOf": [
- {
- "type": "object",
- "required": ["kind", "subject"],
- "additionalProperties": false,
- "properties": {
- "kind": { "const": "user" },
- "subject": { "type": "string", "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$" }
- }
- },
- {
- "type": "object",
- "required": ["kind", "project_id"],
- "additionalProperties": false,
- "properties": {
- "kind": { "const": "project" },
- "project_id": { "type": "string", "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$" }
- }
- },
- {
- "type": "object",
- "required": ["kind"],
- "additionalProperties": false,
- "properties": {
- "kind": { "const": "platform" }
- }
- }
- ]
- },
- "reason": {
- "type": "string",
- "minLength": 1,
- "maxLength": 500,
- "description": "Why it was revoked, when the caller gave one (the owner's text or the service's); absent otherwise."
- },
- "at": {
- "type": "string",
- "format": "date-time",
- "description": "When the revoke was applied (platform_node_principals.revoked_at)."
- }
- }
-}
@@ -23,14 +23,14 @@
@@ -23,14 +23,14 @@
},
},
"sub": {
"sub": {
"type": "string",
"type": "string",
- "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|node:nod_[0-9A-HJKMNP-TV-Z]{26}|agent:agt_[0-9A-HJKMNP-TV-Z]{26})$"
+ "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|link:lnk_[0-9A-HJKMNP-TV-Z]{26}|agent:agt_[0-9A-HJKMNP-TV-Z]{26})$"
},
},
"actor_type": {
"actor_type": {
"enum": [
"enum": [
"service",
"service",
"app",
"app",
"mod",
"mod",
- "node",
+ "link",
"agent"
"agent"
]
]
},
},
new file mode 100644
new file mode 100644
@@ -0,0 +1,69 @@
@@ -0,0 +1,69 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/link/compute.v1.json",
+ "title": "LinkCompute",
+ "description": "Where a person's work can run. Only ready candidates may be picked; a missing rental quote is never a price.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["sources", "candidates"],
+ "properties": {
+ "sources": {
+ "type": "array",
+ "items": { "$ref": "#/$defs/source" }
+ },
+ "candidates": {
+ "type": "array",
+ "items": { "$ref": "#/$defs/candidate" }
+ }
+ },
+ "$defs": {
+ "kind": { "enum": ["personal", "cloud", "exchange", "community"] },
+ "status": { "enum": ["ready", "unavailable", "coming_soon"] },
+ "price": {
+ "type": ["object", "null"],
+ "additionalProperties": false,
+ "required": ["amount_usd", "unit"],
+ "properties": {
+ "amount_usd": { "type": "number", "minimum": 0 },
+ "unit": { "enum": ["hour"] }
+ }
+ },
+ "source": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["kind", "name", "status", "reason"],
+ "properties": {
+ "id": { "type": "string", "minLength": 1 },
+ "principal_id": { "type": "string", "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$" },
+ "kind": { "$ref": "#/$defs/kind" },
+ "name": { "type": "string", "minLength": 1 },
+ "status": { "$ref": "#/$defs/status" },
+ "reason": { "type": ["string", "null"] },
+ "price": { "$ref": "#/$defs/price" },
+ "quoted_at": { "type": "string", "format": "date-time" },
+ "expires_at": { "type": "string", "format": "date-time" }
+ },
+ "if": { "properties": { "kind": { "const": "exchange" }, "status": { "const": "ready" } }, "required": ["kind", "status"] },
+ "then": { "required": ["price", "quoted_at", "expires_at"], "properties": { "price": { "type": "object" } } }
+ },
+ "candidate": {
+ "allOf": [
+ { "$ref": "#/$defs/source" },
+ { "type": "object", "required": ["id", "price"], "properties": { "id": { "type": "string", "minLength": 1 } } }
+ ]
+ }
+ },
+ "examples": [
+ {
+ "sources": [
+ { "kind": "personal", "name": "Your Link machines", "status": "ready", "reason": null },
+ { "kind": "cloud", "name": "OpenVibe cloud machines", "status": "coming_soon", "reason": "No cloud pool is accepting jobs yet." },
+ { "kind": "exchange", "name": "Exchange GPU rentals", "status": "coming_soon", "reason": "A live quote is required.", "price": null },
+ { "kind": "community", "name": "Community machines", "status": "coming_soon", "reason": "Shared placement is being connected." }
+ ],
+ "candidates": [
+ { "id": "dev_01J0000000000000000000000", "kind": "personal", "name": "Studio", "status": "ready", "reason": null, "price": { "amount_usd": 0, "unit": "hour" } }
+ ]
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/network/link-list-result.v1.json",
+ "title": "NetworkLinkListResult",
+ "description": "network.link-list-result@1: GET /api/v1/links on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().",
+ "type": "object",
+ "required": ["nodes", "generated_at"],
+ "additionalProperties": false,
+ "properties": {
+ "nodes": { "type": "array", "items": { "$ref": "link.v1.json" } },
+ "generated_at": { "type": "string", "format": "date-time" }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/network/link-report-request.v1.json",
+ "title": "NetworkLinkReportRequest",
+ "description": "network.link-report-request@1: POST /internal/links/report on OpenVibe.Network (capability network.link.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.",
+ "type": "object",
+ "required": ["source", "nodes"],
+ "additionalProperties": false,
+ "properties": {
+ "source": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$", "description": "The reporting inventory (e.g. the host id)." },
+ "nodes": { "type": "array", "maxItems": 500, "items": { "$ref": "link.v1.json" } }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/network/link.v1.json",
+ "title": "NetworkLink",
+ "description": "network.link@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.",
+ "type": "object",
+ "required": ["id", "roles", "location", "health", "updated_at"],
+ "additionalProperties": false,
+ "properties": {
+ "id": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$" },
+ "name": { "type": "string", "maxLength": 80 },
+ "roles": {
+ "type": "array", "minItems": 1, "uniqueItems": true,
+ "items": { "enum": ["web", "app", "data", "media-worker", "ingest", "edge-probe", "edge-relay", "edge-cache", "gpu", "staging", "ci"] }
+ },
+ "location": {
+ "type": "object",
+ "required": ["region", "country"],
+ "additionalProperties": false,
+ "properties": {
+ "region": { "type": "string", "pattern": "^[a-z]{2}-[a-z]+(-[0-9])?$", "description": "e.g. us-west, eu-central" },
+ "country": { "type": "string", "pattern": "^[A-Z]{2}$" },
+ "city": { "type": "string", "maxLength": 80 },
+ "lat": { "type": "number", "minimum": -90, "maximum": 90 },
+ "lon": { "type": "number", "minimum": -180, "maximum": 180 }
+ }
+ },
+ "provider": { "type": "string", "maxLength": 40 },
+ "beacon": { "type": "string", "format": "uri", "pattern": "^https://", "description": "Timed by clients (GET, tiny body, no caching) to measure round-trip time." },
+ "health": {
+ "type": "object",
+ "required": ["status", "checked_at"],
+ "additionalProperties": false,
+ "properties": {
+ "status": { "enum": ["up", "degraded", "down", "unknown"] },
+ "checked_at": { "type": "string", "format": "date-time" }
+ }
+ },
+ "updated_at": { "type": "string", "format": "date-time" },
+ "cell": { "type": "string", "pattern": "^[a-z]{2,8}-[0-9]{1,3}$", "description": "Cell the node belongs to, e.g. wnam-1." }
+ }
+}
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/network/machine-pairing.v1.json",
"$id": "https://openvibe.network/contracts/network/machine-pairing.v1.json",
"title": "NetworkMachinePairing",
"title": "NetworkMachinePairing",
- "description": "network.machine-pairing@1: a one-time code that pairs a machine with the person's account (POST /api/v1/machines/pairings, capability machines.manage). The code is shown once, expires, and works once: the machine redeems it and becomes the person's. The command is what the owner runs on the machine (openvibe.bot/install). The code is never stored in the clear and never returned again.",
+ "description": "network.machine-pairing@1: a one-time code that pairs a machine with the person's account (POST /api/v1/machines/pairings, capability machines.manage). The code is shown once, expires, and works once: the machine redeems it and becomes the person's. The command is what the owner runs on the machine (openvibe.link/install). The code is never stored in the clear and never returned again.",
"type": "object",
"type": "object",
"required": ["code", "expires_at", "installer"],
"required": ["code", "expires_at", "installer"],
"additionalProperties": false,
"additionalProperties": false,
@@ -21,7 +21,7 @@
@@ -21,7 +21,7 @@
"installer": {
"installer": {
"type": "string",
"type": "string",
"maxLength": 500,
"maxLength": 500,
- "description": "The command the owner runs on the machine (openvibe.bot/install, with the network, the pairing id and the code)."
+ "description": "The command the owner runs on the machine (openvibe.link/install, with the network, the pairing id and the code)."
}
}
}
}
}
}
@@ -14,8 +14,8 @@
@@ -14,8 +14,8 @@
},
},
"principal_id": {
"principal_id": {
"type": "string",
"type": "string",
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "The machine's node principal on Network (nod_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for."
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The machine's node principal on Network (lnk_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for."
},
},
"name": { "type": "string", "maxLength": 80, "description": "The name the owner gave it. Absent when they gave none." },
"name": { "type": "string", "maxLength": 80, "description": "The name the owner gave it. Absent when they gave none." },
"kind": {
"kind": {
deleted file mode 100644
deleted file mode 100644
@@ -1,13 +0,0 @@
@@ -1,13 +0,0 @@
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/network/node-list-result.v1.json",
- "title": "NetworkNodeListResult",
- "description": "network.node-list-result@1: GET /api/v1/nodes on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().",
- "type": "object",
- "required": ["nodes", "generated_at"],
- "additionalProperties": false,
- "properties": {
- "nodes": { "type": "array", "items": { "$ref": "node.v1.json" } },
- "generated_at": { "type": "string", "format": "date-time" }
- }
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,13 +0,0 @@
@@ -1,13 +0,0 @@
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/network/node-report-request.v1.json",
- "title": "NetworkNodeReportRequest",
- "description": "network.node-report-request@1: POST /internal/nodes/report on OpenVibe.Network (capability network.node.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.",
- "type": "object",
- "required": ["source", "nodes"],
- "additionalProperties": false,
- "properties": {
- "source": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$", "description": "The reporting inventory (e.g. the host id)." },
- "nodes": { "type": "array", "maxItems": 500, "items": { "$ref": "node.v1.json" } }
- }
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,42 +0,0 @@
@@ -1,42 +0,0 @@
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/network/node.v1.json",
- "title": "NetworkNode",
- "description": "network.node@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.",
- "type": "object",
- "required": ["id", "roles", "location", "health", "updated_at"],
- "additionalProperties": false,
- "properties": {
- "id": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$" },
- "name": { "type": "string", "maxLength": 80 },
- "roles": {
- "type": "array", "minItems": 1, "uniqueItems": true,
- "items": { "enum": ["web", "app", "data", "media-worker", "ingest", "edge-probe", "edge-relay", "edge-cache", "gpu", "staging", "ci"] }
- },
- "location": {
- "type": "object",
- "required": ["region", "country"],
- "additionalProperties": false,
- "properties": {
- "region": { "type": "string", "pattern": "^[a-z]{2}-[a-z]+(-[0-9])?$", "description": "e.g. us-west, eu-central" },
- "country": { "type": "string", "pattern": "^[A-Z]{2}$" },
- "city": { "type": "string", "maxLength": 80 },
- "lat": { "type": "number", "minimum": -90, "maximum": 90 },
- "lon": { "type": "number", "minimum": -180, "maximum": 180 }
- }
- },
- "provider": { "type": "string", "maxLength": 40 },
- "beacon": { "type": "string", "format": "uri", "pattern": "^https://", "description": "Timed by clients (GET, tiny body, no caching) to measure round-trip time." },
- "health": {
- "type": "object",
- "required": ["status", "checked_at"],
- "additionalProperties": false,
- "properties": {
- "status": { "enum": ["up", "degraded", "down", "unknown"] },
- "checked_at": { "type": "string", "format": "date-time" }
- }
- },
- "updated_at": { "type": "string", "format": "date-time" },
- "cell": { "type": "string", "pattern": "^[a-z]{2,8}-[0-9]{1,3}$", "description": "Cell the node belongs to, e.g. wnam-1." }
- }
-}
@@ -117,7 +117,7 @@
@@ -117,7 +117,7 @@
{
{
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
- "description": "Device to server: wall-clock second `second` of the job has fully elapsed. Metering (platform.usage-sample@1, written through billing.usage.record by whoever terminates the link: OpenVibe.Bot now, Fabric later): second n is [started_ms + n*1000, started_ms + (n+1)*1000) on the worker's monotonic clock. A worker sends `job_usage` for n only once that second has fully elapsed, so every `job_usage` stands for quantity 1; the partial last second is never sent here, only in `job_exit`. Each second is exactly one reading: `id` and `idempotency_key` `run:<job id>:<n>`, `service` `run`, `operation` `function.invoke`, `unit` `s`, `quantity` 1 (the partial last second: (wall_ms mod 1000)/1000, never 0), `at` new Date(started_ms + n*1000).toISOString(), `resource` the job id, `node` the worker's device id, `source` `openvibe-node.worker`, and `project`, `subject` and `provider` from the dispatcher's own record of the job. No field depends on when or how often a frame was sent, and no rating fields (`cost_estimate`, `free_allowance_used`, `vibes_charged`) are set, because Billing keeps the first reading under a key, replays an identical one and refuses a different one (409): a resend after a reconnect, a `job_usage` and the `job_exit` backfill of the same second, and two writers of one job all land on one record.",
+ "description": "Device to server: wall-clock second `second` of the job has fully elapsed. Metering (platform.usage-sample@1, written through billing.usage.record by whoever terminates the link: OpenVibe.Bot now, Fabric later): second n is [started_ms + n*1000, started_ms + (n+1)*1000) on the worker's monotonic clock. A worker sends `job_usage` for n only once that second has fully elapsed, so every `job_usage` stands for quantity 1; the partial last second is never sent here, only in `job_exit`. Each second is exactly one reading: `id` and `idempotency_key` `run:<job id>:<n>`, `service` `run`, `operation` `function.invoke`, `unit` `s`, `quantity` 1 (the partial last second: (wall_ms mod 1000)/1000, never 0), `at` new Date(started_ms + n*1000).toISOString(), `resource` the job id, `node` the worker's device id, `source` `openvibe-link.worker`, and `project`, `subject` and `provider` from the dispatcher's own record of the job. No field depends on when or how often a frame was sent, and no rating fields (`cost_estimate`, `free_allowance_used`, `vibes_charged`) are set, because Billing keeps the first reading under a key, replays an identical one and refuses a different one (409): a resend after a reconnect, a `job_usage` and the `job_exit` backfill of the same second, and two writers of one job all land on one record.",
"required": [
"required": [
"v",
"v",
"seq",
"seq",
new file mode 100644
new file mode 100644
@@ -0,0 +1,194 @@
@@ -0,0 +1,194 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/platform/link-capabilities.v1.json",
+ "title": "LinkCapabilities",
+ "description": "Link machine hardware, placement regions, tags, and operating costs (T1 Universal Fabric; ADR-034 proposed).",
+ "$comment": "Additive optional fields may extend this v1 contract.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "node_id",
+ "cpu",
+ "arch",
+ "memory_mb",
+ "storage",
+ "network",
+ "regions",
+ "tags",
+ "costs"
+ ],
+ "properties": {
+ "node_id": {
+ "type": "string"
+ },
+ "cpu": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "cores": {
+ "type": "number",
+ "minimum": 0
+ },
+ "threads": {
+ "type": "integer",
+ "minimum": 0
+ }
+ },
+ "required": [
+ "cores"
+ ]
+ },
+ "arch": {
+ "type": "string",
+ "description": "CPU architecture"
+ },
+ "memory_mb": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "gpu": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "model": {
+ "type": "string"
+ },
+ "count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "vram_mb": {
+ "type": "integer",
+ "minimum": 0
+ }
+ },
+ "required": [
+ "model",
+ "count"
+ ]
+ },
+ "storage": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "capacity_gb": {
+ "type": "number",
+ "minimum": 0
+ },
+ "available_gb": {
+ "type": "number",
+ "minimum": 0
+ },
+ "kind": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "capacity_gb"
+ ]
+ },
+ "network": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "ingress_mbps": {
+ "type": "number",
+ "minimum": 0
+ },
+ "egress_mbps": {
+ "type": "number",
+ "minimum": 0
+ }
+ },
+ "required": [
+ "ingress_mbps",
+ "egress_mbps"
+ ]
+ },
+ "regions": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "minItems": 1
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "costs": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "per_hour_usd": {
+ "type": "number",
+ "minimum": 0,
+ "description": "Estimated USD"
+ },
+ "egress_per_gb_usd": {
+ "type": "number",
+ "minimum": 0,
+ "description": "Estimated USD"
+ }
+ },
+ "required": [
+ "per_hour_usd"
+ ]
+ },
+ "capabilities": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"
+ },
+ "description": "What the machine offers, in the platform.resource-offer@1 capability form (e.g. node:http, worker:ffmpeg)."
+ },
+ "agent_version": {
+ "type": "string",
+ "maxLength": 40,
+ "description": "Version of the node agent that reported."
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "examples": [
+ {
+ "node_id": "node-1",
+ "cpu": {
+ "cores": 8,
+ "threads": 16
+ },
+ "arch": "x86_64",
+ "memory_mb": 16384,
+ "gpu": {
+ "model": "L4",
+ "count": 1,
+ "vram_mb": 24576
+ },
+ "storage": {
+ "capacity_gb": 500,
+ "available_gb": 300,
+ "kind": "ssd"
+ },
+ "network": {
+ "ingress_mbps": 1000,
+ "egress_mbps": 1000
+ },
+ "regions": [
+ "us-west"
+ ],
+ "tags": [
+ "gpu",
+ "edge"
+ ],
+ "costs": {
+ "per_hour_usd": 0.2,
+ "egress_per_gb_usd": 0.01
+ }
+ }
+ ]
+}
deleted file mode 100644
deleted file mode 100644
@@ -1,194 +0,0 @@
@@ -1,194 +0,0 @@
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/platform/node-capabilities.v1.json",
- "title": "NodeCapabilities",
- "description": "Node hardware, placement regions, tags, and operating costs (T1 Universal Fabric; ADR-034 proposed).",
- "$comment": "Additive optional fields may extend this v1 contract.",
- "type": "object",
- "additionalProperties": false,
- "required": [
- "node_id",
- "cpu",
- "arch",
- "memory_mb",
- "storage",
- "network",
- "regions",
- "tags",
- "costs"
- ],
- "properties": {
- "node_id": {
- "type": "string"
- },
- "cpu": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "cores": {
- "type": "number",
- "minimum": 0
- },
- "threads": {
- "type": "integer",
- "minimum": 0
- }
- },
- "required": [
- "cores"
- ]
- },
- "arch": {
- "type": "string",
- "description": "CPU architecture"
- },
- "memory_mb": {
- "type": "integer",
- "minimum": 0
- },
- "gpu": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "model": {
- "type": "string"
- },
- "count": {
- "type": "integer",
- "minimum": 0
- },
- "vram_mb": {
- "type": "integer",
- "minimum": 0
- }
- },
- "required": [
- "model",
- "count"
- ]
- },
- "storage": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "capacity_gb": {
- "type": "number",
- "minimum": 0
- },
- "available_gb": {
- "type": "number",
- "minimum": 0
- },
- "kind": {
- "type": "string"
- }
- },
- "required": [
- "capacity_gb"
- ]
- },
- "network": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "ingress_mbps": {
- "type": "number",
- "minimum": 0
- },
- "egress_mbps": {
- "type": "number",
- "minimum": 0
- }
- },
- "required": [
- "ingress_mbps",
- "egress_mbps"
- ]
- },
- "regions": {
- "type": "array",
- "items": {
- "type": "string"
- },
- "minItems": 1
- },
- "tags": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "costs": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "per_hour_usd": {
- "type": "number",
- "minimum": 0,
- "description": "Estimated USD"
- },
- "egress_per_gb_usd": {
- "type": "number",
- "minimum": 0,
- "description": "Estimated USD"
- }
- },
- "required": [
- "per_hour_usd"
- ]
- },
- "capabilities": {
- "type": "array",
- "items": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"
- },
- "description": "What the machine offers, in the platform.resource-offer@1 capability form (e.g. node:http, worker:ffmpeg)."
- },
- "agent_version": {
- "type": "string",
- "maxLength": 40,
- "description": "Version of the node agent that reported."
- },
- "updated_at": {
- "type": "string",
- "format": "date-time"
- }
- },
- "examples": [
- {
- "node_id": "node-1",
- "cpu": {
- "cores": 8,
- "threads": 16
- },
- "arch": "x86_64",
- "memory_mb": 16384,
- "gpu": {
- "model": "L4",
- "count": 1,
- "vram_mb": 24576
- },
- "storage": {
- "capacity_gb": 500,
- "available_gb": 300,
- "kind": "ssd"
- },
- "network": {
- "ingress_mbps": 1000,
- "egress_mbps": 1000
- },
- "regions": [
- "us-west"
- ],
- "tags": [
- "gpu",
- "edge"
- ],
- "costs": {
- "per_hour_usd": 0.2,
- "egress_per_gb_usd": 0.01
- }
- }
- ]
-}
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/platform/resource-offer.v1.json",
"$id": "https://openvibe.network/contracts/platform/resource-offer.v1.json",
"title": "ResourceOffer",
"title": "ResourceOffer",
- "description": "What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.node@1) deliberately leaves out.",
+ "description": "What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.link@1) deliberately leaves out.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
"required": [
"required": [
@@ -33,7 +33,7 @@
@@ -33,7 +33,7 @@
},
},
"node_id": {
"node_id": {
"type": "string",
"type": "string",
- "description": "network.node@1 id, for kind node"
+ "description": "network.link@1 id, for kind node"
},
},
"provider": {
"provider": {
"type": "string",
"type": "string",
@@ -71,7 +71,7 @@
@@ -71,7 +71,7 @@
"type": "string",
"type": "string",
"pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"
"pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"
},
},
- "description": "e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2. 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 Link'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. Beside them, a kind harness names `runtime:<class>` for each platform.runtime-class@1 class it can run (runtime:function, runtime:code, runtime:browser, runtime:linux, runtime:desktop, runtime:gpu); these are ordinary capability names, not reserved, and OpenVibe.Codes already advertises the classes it offers."
+ "description": "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 Link's job worker, one per class: worker:function, worker:code, worker:browser, worker:linux, worker:desktop and worker:gpu ($defs.reservedWorkerCapabilities). A Link advertises the name of each class it runs (Link's status.capabilities.worker); every other `worker:` name (worker:ffmpeg, worker:ai-gpu) is an ordinary capability name. Beside them, a kind harness names `runtime:<class>` for each platform.runtime-class@1 class it can run (runtime:function, runtime:code, runtime:browser, runtime:linux, runtime:desktop, runtime:gpu); these are ordinary capability names, not reserved, and OpenVibe.Codes already advertises the classes it offers."
},
},
"capacity": {
"capacity": {
"type": "object",
"type": "object",
@@ -146,7 +146,7 @@
@@ -146,7 +146,7 @@
"quantity": 1,
"quantity": 1,
"unit": "s",
"unit": "s",
"at": "2026-10-02T10:00:00.250Z",
"at": "2026-10-02T10:00:00.250Z",
- "source": "openvibe-node.worker"
+ "source": "openvibe-link.worker"
}
}
]
]
}
}
@@ -132,7 +132,7 @@
@@ -132,7 +132,7 @@
"required": ["node", "provider", "region"],
"required": ["node", "provider", "region"],
"properties": {
"properties": {
"node": { "type": "string", "minLength": 1, "maxLength": 128, "description": "The worker's device id (the usage reading's `node`)." },
"node": { "type": "string", "minLength": 1, "maxLength": 128, "description": "The worker's device id (the usage reading's `node`)." },
- "provider": { "type": ["string", "null"], "maxLength": 128, "description": "The offer's provider (the usage reading's `provider`); null for a first-party node." },
+ "provider": { "type": ["string", "null"], "maxLength": 128, "description": "The offer's provider (the usage reading's `provider`); null for a first-party machine." },
"region": { "type": ["string", "null"], "maxLength": 64 }
"region": { "type": ["string", "null"], "maxLength": 64 }
}
}
},
},
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/watch/watch.v1.json",
"$id": "https://openvibe.network/contracts/watch/watch.v1.json",
"title": "Watch",
"title": "Watch",
- "description": "watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Node probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.",
+ "description": "watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Link probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
"required": [
"required": [
@@ -17,7 +17,7 @@
@@ -17,7 +17,7 @@
},
},
"node_id": {
"node_id": {
"type": "string",
"type": "string",
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
"description": "Must equal the node principal in the Network token; a mismatch is refused and audited."
"description": "Must equal the node principal in the Network token; a mismatch is refused and audited."
},
},
"tunnel_token": {
"tunnel_token": {
@@ -52,8 +52,8 @@
@@ -52,8 +52,8 @@
},
},
"node_id": {
"node_id": {
"type": "string",
"type": "string",
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "Optional: bind the share to this Node principal now. Omit to bind it when the first tunnel connects."
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "Optional: bind the share to this Link principal now. Omit to bind it when the first tunnel connects."
},
},
"region": {
"region": {
"type": "string",
"type": "string",
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/website/share.v1.json",
"$id": "https://openvibe.network/contracts/website/share.v1.json",
"title": "WebsiteShare",
"title": "WebsiteShare",
- "description": "website.share@1: one Share on OpenVibe.Website Connect, a local port published at a stable hostname through the edge relay. The record says who owns it, which project scopes it, which Node tunnel serves it, which local target it may reach, which policy version the edge enforces and where it is in its life (draft, review, active, paused, revoked). The tunnel token, the request bodies the inspector captures and the policy's password hash are never part of this document.",
+ "description": "website.share@1: one Share on OpenVibe.Website Connect, a local port published at a stable hostname through the edge relay. The record says who owns it, which project scopes it, which Link tunnel serves it, which local target it may reach, which policy version the edge enforces and where it is in its life (draft, review, active, paused, revoked). The tunnel token, the request bodies the inspector captures and the policy's password hash are never part of this document.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
"required": [
"required": [
@@ -44,7 +44,7 @@
@@ -44,7 +44,7 @@
"string",
"string",
"null"
"null"
],
],
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
"description": "The Network node principal expected to serve the tunnel; null until one is bound."
"description": "The Network node principal expected to serve the tunnel; null until one is bound."
},
},
"name": {
"name": {
@@ -122,7 +122,7 @@
@@ -122,7 +122,7 @@
"null"
"null"
],
],
"format": "date-time",
"format": "date-time",
- "description": "The last successful Node liveness check; null until the first tunnel."
+ "description": "The last successful Link liveness check; null until the first tunnel."
},
},
"created_at": {
"created_at": {
"type": "string",
"type": "string",
@@ -37,7 +37,7 @@ service id); **Runtime** is `STATUS.json.runtime`; **Database** is inferred from
@@ -37,7 +37,7 @@ service id); **Runtime** is `STATUS.json.runtime`; **Database** is inferred from
| OpenVibe.MediaHub | Video | video.*, pics.*, download.*, mediahub.* | node 22 | PostgreSQL | openvibe.video, openvibe.download, openvibe.pics, openvibe.network | contracts v0.222.0, sdk v0.67.0, shared v4.17.0 | deployed | live | W20 |
| OpenVibe.MediaHub | Video | video.*, pics.*, download.*, mediahub.* | node 22 | PostgreSQL | openvibe.video, openvibe.download, openvibe.pics, openvibe.network | contracts v0.222.0, sdk v0.67.0, shared v4.17.0 | deployed | live | W20 |
| OpenVibe.Network | Network | identity.*, network.*, theme.*, notification.* | node 22 | PostgreSQL | openvibe.network, auth.openvibe.network, api.openvibe.network, admin.openvibe.network, my.openvibe.network, themes.openvibe.network | contracts v0.222.0, sdk v0.67.0, shared v4.17.0 | deployed | live | — |
| OpenVibe.Network | Network | identity.*, network.*, theme.*, notification.* | node 22 | PostgreSQL | openvibe.network, auth.openvibe.network, api.openvibe.network, admin.openvibe.network, my.openvibe.network, themes.openvibe.network | contracts v0.222.0, sdk v0.67.0, shared v4.17.0 | deployed | live | — |
| OpenVibe.News | News | news.* | node 22 | PostgreSQL | openvibe.news | contracts v0.222.0, sdk v0.67.0, shared v4.17.0, publishing v1.4.1 | deployed | live | W17 |
| OpenVibe.News | News | news.* | node 22 | PostgreSQL | openvibe.news | contracts v0.222.0, sdk v0.67.0, shared v4.17.0, publishing v1.4.1 | deployed | live | W17 |
-| OpenVibe.Node | — | — | go 1.24 (one static binary per platform) | none | — | — | not deployed | — | T15 |
+| OpenVibe.Link | — | — | go 1.24 (one static binary per platform) | none | — | — | not deployed | — | T15 |
| OpenVibe.Play | Play | play.* | node 22 | PostgreSQL | play.openvibe.network | contracts v0.222.0, sdk v0.67.0, shared v4.17.0 | deployed | live | T8 |
| OpenVibe.Play | Play | play.* | node 22 | PostgreSQL | play.openvibe.network | contracts v0.222.0, sdk v0.67.0, shared v4.17.0 | deployed | live | T8 |
| OpenVibe.Publishing | OpenVibe.Publishing | publishing | node >=20 | none | — | contracts v0.222.0, sdk v0.67.0, shared v4.17.0 | not deployed | library | W15 |
| OpenVibe.Publishing | OpenVibe.Publishing | publishing | node >=20 | none | — | contracts v0.222.0, sdk v0.67.0, shared v4.17.0 | not deployed | library | W15 |
| OpenVibe.Quest | Quest | quest.* | node 22 | PostgreSQL | openvibe.quest | contracts v0.222.0, sdk v0.67.0, shared v4.17.0, publishing v1.4.0 | deployed | live | W20 |
| OpenVibe.Quest | Quest | quest.* | node 22 | PostgreSQL | openvibe.quest | contracts v0.222.0, sdk v0.67.0, shared v4.17.0, publishing v1.4.0 | deployed | live | W20 |
@@ -66,7 +66,7 @@ OpenVibe.Tools that day found:
@@ -66,7 +66,7 @@ OpenVibe.Tools that day found:
### Browser-only tools may get server engines
### Browser-only tools may get server engines
-A pure transform whose engine also runs in Node gets an opt-in server engine for the API: JSON,
+A pure transform whose engine also runs in Link gets an opt-in server engine for the API: JSON,
minify and beautify, case, slug, hash, base64 and the like. Its descriptor then says
minify and beautify, case, slug, hash, base64 and the like. Its descriptor then says
`execution: client` and `api: true`. The page stays browser-only, so "nothing leaves your browser"
`execution: client` and `api: true`. The page stays browser-only, so "nothing leaves your browser"
stays true for people using the page. A tool without such an engine says `api: false`.
stays true for people using the page. A tool without such an engine says `api: false`.
@@ -19,7 +19,7 @@ What exists already maps onto the primitives of a cloud:
@@ -19,7 +19,7 @@ What exists already maps onto the primitives of a cloud:
| Developer surface | Codes (portal, apps, playgrounds, docs), openvibe-sdk v0.13, Contracts v0.75 | ADR-002, ADR-008 |
| Developer surface | Codes (portal, apps, playgrounds, docs), openvibe-sdk v0.13, Contracts v0.75 | ADR-002, ADR-008 |
| Extensions | Mods with their own principals, budgets and grants; Tools platform API | ADR-013, ADR-027 |
| Extensions | Mods with their own principals, budgets and grants; Tools platform API | ADR-013, ADR-027 |
-Everything runs on one host: about 25 Node services, one SQLite database each, as systemd units. Measured load is about 1% of what SQLite handles (ADR-007 amendment, 2026-09-26). Deploys are manual `ovhost deploy` runs.
+Everything runs on one host: about 25 Link services, one SQLite database each, as systemd units. Measured load is about 1% of what SQLite handles (ADR-007 amendment, 2026-09-26). Deploys are manual `ovhost deploy` runs.
At a much larger size, the database engine and the orchestrator are not what is expensive to change. The expensive things are **boundaries**: how tenants are separated, how resources are named, how access is decided, what the API contract is, and how usage is counted. Each of those touches every row, every route and every event, so changing it late means migrating everything. Today they are cheap to set.
At a much larger size, the database engine and the orchestrator are not what is expensive to change. The expensive things are **boundaries**: how tenants are separated, how resources are named, how access is decided, what the API contract is, and how usage is counted. Each of those touches every row, every route and every event, so changing it late means migrating everything. Today they are cheap to set.
@@ -9,7 +9,7 @@ isolation belongs to Stage C) and ADR-046 (offers, requirements, placement, sign
@@ -9,7 +9,7 @@ isolation belongs to Stage C) and ADR-046 (offers, requirements, placement, sign
- **Plan T14** (lines 559–583): *"Finish line: any workload — a function, code, a browser session, a Linux or desktop
- **Plan T14** (lines 559–583): *"Finish line: any workload — a function, code, a browser session, a Linux or desktop
machine, a GPU job — runs in a sandbox with metering, limits, cancellation and streaming, placed by the Fabric on
machine, a GPU job — runs in a sandbox with metering, limits, cancellation and streaming, placed by the Fabric on
- the cheapest capability that works; the Node runtime runs the same workloads on official servers and on users' own
+ the cheapest capability that works; the Link runtime runs the same workloads on official servers and on users' own
machines under their trust policy."* And: *"**Run is not a Host detail.** Host deploys persistent applications; Run
machines under their trust policy."* And: *"**Run is not a Host detail.** Host deploys persistent applications; Run
executes isolated jobs/environments. They can share machines; they are separate authorities (ADR-036 rewritten
executes isolated jobs/environments. They can share machines; they are separate authorities (ADR-036 rewritten
around this expanded purpose)."*
around this expanded purpose)."*
@@ -77,16 +77,16 @@ Run states `platform.workload-requirements@1` and reads the `platform.placement-
@@ -77,16 +77,16 @@ Run states `platform.workload-requirements@1` and reads the `platform.placement-
Run writes no placer of its own (ADR-046: "A service states requirements and reads results; it does not rank
Run writes no placer of its own (ADR-046: "A service states requirements and reads results; it does not rank
offers."). The plan's §2.1.11 cross-reference is the Fabric section as written in ADR-046/T1, not a separate scheme.
offers."). The plan's §2.1.11 cross-reference is the Fabric section as written in ADR-046/T1, not a separate scheme.
-**Node descriptor → offers.** When Run turns an OpenVibe Link into Fabric offers it applies the mapping the Node
+**Link descriptor → offers.** When Run turns an OpenVibe Link into Fabric offers it applies the mapping the Link
protocol already fixes (OpenVibe Link `docs/protocol.md:310–321`):
protocol already fixes (OpenVibe Link `docs/protocol.md:310–321`):
-| Node descriptor field | Fabric offer | Example |
+| Link descriptor field | Fabric offer | Example |
| --- | --- | --- |
| --- | --- | --- |
| `capabilities.worker.runtime_classes` | `resource-offer.capabilities` item `worker:<class>` | `worker:function`, `worker:code` |
| `capabilities.worker.runtime_classes` | `resource-offer.capabilities` item `worker:<class>` | `worker:function`, `worker:code` |
| `capabilities.worker.max_jobs` | `runtime-offer.limits.concurrency` | 1 |
| `capabilities.worker.max_jobs` | `runtime-offer.limits.concurrency` | 1 |
| `capabilities.worker.max_ttl_ms` | `runtime-offer.limits.duration_seconds` | 600 |
| `capabilities.worker.max_ttl_ms` | `runtime-offer.limits.duration_seconds` | 600 |
| device `node_id` / cell | `resource-offer.node_id` / `resource-offer.cell` | `node_01H…` / `wnam-1` |
| device `node_id` / cell | `resource-offer.node_id` / `resource-offer.cell` | `node_01H…` / `wnam-1` |
-| worker trust (local policy) | `resource-offer.trust` | `user-owned` for a user's own Node |
+| worker trust (local policy) | `resource-offer.trust` | `user-owned` for a user's own Link |
### 4. The cost ladder
### 4. The cost ladder
@@ -130,7 +130,7 @@ Every job, on every adapter, is held to the same guarantees (plan T14, lines 572
@@ -130,7 +130,7 @@ Every job, on every adapter, is held to the same guarantees (plan T14, lines 572
1. **Isolation mechanism.** The plan requires more than a container for hostile arbitrary code but names no
1. **Isolation mechanism.** The plan requires more than a container for hostile arbitrary code but names no
mechanism. Micro-VM (Firecracker/Kata), a userspace kernel (gVisor) and namespace/seccomp sandboxes are all open;
mechanism. Micro-VM (Firecracker/Kata), a userspace kernel (gVisor) and namespace/seccomp sandboxes are all open;
the Link worker's current `function` isolation is namespaces plus a seccomp allowlist.
the Link worker's current `function` isolation is namespaces plus a seccomp allowlist.
-2. **Snapshot/resume backend.** Whether a resumed environment's state lives in Media, a Node-local disk, or a
+2. **Snapshot/resume backend.** Whether a resumed environment's state lives in Media, a Link-local disk, or a
provider's own snapshot is open.
provider's own snapshot is open.
3. **Idle-suspend policy.** Who decides to suspend — Run, the node's local policy, or a placement objective — is
3. **Idle-suspend policy.** Who decides to suspend — Run, the node's local policy, or a placement objective — is
open.
open.
@@ -147,6 +147,6 @@ Every job, on every adapter, is held to the same guarantees (plan T14, lines 572
@@ -147,6 +147,6 @@ Every job, on every adapter, is held to the same guarantees (plan T14, lines 572
`linux`, `desktop` and `gpu` are reserved names with no worker.
`linux`, `desktop` and `gpu` are reserved names with no worker.
- Cells are a single host (`cell = wnam-1`) today; O24's second physical cell, the WireGuard mesh and geo placement
- Cells are a single host (`cell = wnam-1`) today; O24's second physical cell, the WireGuard mesh and geo placement
are not built (plan T14, lines 580–581).
are not built (plan T14, lines 580–581).
-- Per-second metering is proven only for Node jobs; no Billing consumer reads Run usage on `origin/main` yet.
+- Per-second metering is proven only for Link jobs; no Billing consumer reads Run usage on `origin/main` yet.
- The provider adapters above are the plan's list, not deployed adapters; no managed-browser, external-sandbox or
- The provider adapters above are the plan's list, not deployed adapters; no managed-browser, external-sandbox or
external-GPU offer producer exists.
external-GPU offer producer exists.
@@ -39,7 +39,7 @@ a Cozmo-based robot).
@@ -39,7 +39,7 @@ a Cozmo-based robot).
robot and reaches it over the robot's own link (Cozmo first through PyCozmo; later Vector, Tello, Sphero, LEGO); a
robot and reaches it over the robot's own link (Cozmo first through PyCozmo; later Vector, Tello, Sphero, LEGO); a
bridge joins the robot's network on one interface and reaches OpenVibe on another. **Server-side**: Bot itself
bridge joins the robot's network on one interface and reaches OpenVibe on another. **Server-side**: Bot itself
connects to ONVIF/RTSP cameras (credentials by secret reference, never in the profile). The agent is a Python 3 package
connects to ONVIF/RTSP cameras (credentials by secret reference, never in the profile). The agent is a Python 3 package
- (the Pi, PyCozmo and the kit libraries are Python), installed by `curl -fsSL https://openvibe.bot/install | sh` into a
+ (the Pi, PyCozmo and the kit libraries are Python), installed by `curl -fsSL https://openvibe.link/install | sh` into a
virtual environment with a systemd unit and the hardware watchdog.
virtual environment with a systemd unit and the hardware watchdog.
4. **Control runs over one outbound WebSocket per device; video over WHIP.** The device dials Bot
4. **Control runs over one outbound WebSocket per device; video over WHIP.** The device dials Bot
(`wss://openvibe.bot/device`), authenticates with its credential, and keeps one connection for commands, telemetry and
(`wss://openvibe.bot/device`), authenticates with its credential, and keeps one connection for commands, telemetry and
@@ -6,7 +6,7 @@ the first new adapters (OpenCode, Antigravity CLI), capability-aware routing, vi
@@ -6,7 +6,7 @@ the first new adapters (OpenCode, Antigravity CLI), capability-aware routing, vi
(headless browser + virtual desktop, screenshots)" ahead of "ADR-044 (task contract, adapter interface, modes,
(headless browser + virtual desktop, screenshots)" ahead of "ADR-044 (task contract, adapter interface, modes,
signals, verification, trust)", and the engine's harness work is paused where it stands. Contract: `platform.task@1`.
signals, verification, trust)", and the engine's harness work is paused where it stands. Contract: `platform.task@1`.
Builds on ADR-034 §2 (projects and resource names), §5 (control and data plane) and §9 (metering), on ADR-046 (the
Builds on ADR-034 §2 (projects and resource names), §5 (control and data plane) and §9 (metering), on ADR-046 (the
-placer, its objectives and the five trust classes), on ADR-012 (one Billing authority) and on ADR-043 (Node control
+placer, its objectives and the five trust classes), on ADR-012 (one Billing authority) and on ADR-043 (Link control
levels and the local kill switch). For the owner's review.
levels and the local kill switch). For the owner's review.
## Context and current evidence
## Context and current evidence
@@ -112,7 +112,7 @@ ADR-046 §4's five classes apply unchanged: `first-party`, `user-owned`, `partne
@@ -112,7 +112,7 @@ ADR-046 §4's five classes apply unchanged: `first-party`, `user-owned`, `partne
data on OpenVibe — `first-party`, and a `user-owned` node only when the deployment allows it — which the plan
data on OpenVibe — `first-party`, and a `user-owned` node only when the deployment allows it — which the plan
describes as "only agents that keep your data on OpenVibe: its own runtime and open models on its hardware". Local
describes as "only agents that keep your data on OpenVibe: its own runtime and open models on its hardware". Local
execution on a person's own machine goes through OpenVibe Link under ADR-043's control levels (Observe / Ask /
execution on a person's own machine goes through OpenVibe Link under ADR-043's control levels (Observe / Ask /
-Trusted / Full control), a local indicator and a kill switch the owner holds; Node's local policy always overrides a
+Trusted / Full control), a local indicator and a kill switch the owner holds; Link's local policy always overrides a
cloud command (plan T14).
cloud command (plan T14).
### 7. Progress, cancel and webhooks
### 7. Progress, cancel and webhooks
@@ -146,7 +146,7 @@ Actor plans and routes; it does not become an authority. The plan's own list of
@@ -146,7 +146,7 @@ Actor plans and routes; it does not become an authority. The plan's own list of
| Coding work | Codes (the one coding-harness router) |
| Coding work | Codes (the one coding-harness router) |
| Persistent conditions | Watch |
| Persistent conditions | Watch |
| Cloud execution | Run (`platform.job@1` / `platform.job-frame@1`) |
| Cloud execution | Run (`platform.job@1` / `platform.job-frame@1`) |
-| Local execution and computer control | Node (ADR-043 control levels, kill switch) |
+| Local execution and computer control | Link (ADR-043 control levels, kill switch) |
| Resource control | Services (`common.resource-control-request@1`, ADR-048) |
| Resource control | Services (`common.resource-control-request@1`, ADR-048) |
| Artifacts | Media |
| Artifacts | Media |
| Robots | Bot |
| Robots | Bot |
@@ -1,26 +1,26 @@
@@ -1,26 +1,26 @@
# ADR-045: The Link is OpenVibe's universal runtime
# ADR-045: The Link is OpenVibe's universal runtime
-**Status:** Proposed 2026-10-05 (plan §8: *"ADR-045 (Node; **revise: Node is the universal runtime, not a
+**Status:** Proposed 2026-10-05 (plan §8: *"ADR-045 (Link; **revise: Link is the universal runtime, not a
Bot/Actor helper**)"*; plan T14, lines 575–579). For the owner's review. Builds on ADR-043 (Bot — devices, pairing,
Bot/Actor helper**)"*; plan T14, lines 575–579). For the owner's review. Builds on ADR-043 (Bot — devices, pairing,
control and safety), ADR-046 §5 (the `worker:<class>` capability names) and ADR-048 (OVRN and authority boundaries).
control and safety), ADR-046 §5 (the `worker:<class>` capability names) and ADR-048 (OVRN and authority boundaries).
-ADR-043 stays the robot/device half of the same Node; this record is the runtime half, and amends ADR-043's credential
+ADR-043 stays the robot/device half of the same Link; this record is the runtime half, and amends ADR-043's credential
paragraph: Network owns the node/device principal and the pairing credential, Bot keeps the robot binding and control
paragraph: Network owns the node/device principal and the pairing credential, Bot keeps the robot binding and control
link.
link.
## Context and current evidence
## Context and current evidence
-- **The plan revises ADR-045.** Plan §8 (line 986) calls ADR-045 written but to be revised: *"Node is the universal
+- **The plan revises ADR-045.** Plan §8 (line 986) calls ADR-045 written but to be revised: *"Link is the universal
runtime, not a Bot/Actor helper"*. `origin/main` has no ADR-045 file in any repository, so the revision is unstated
runtime, not a Bot/Actor helper"*. `origin/main` has no ADR-045 file in any repository, so the revision is unstated
in the contract repository.
in the contract repository.
-- **What the plan says the Link is.** Plan T14, lines 575–579: *"**Node runtime** (ADR-045 revised: Node is the
+- **What the plan says the Link is.** Plan T14, lines 575–579: *"**Link runtime** (ADR-045 revised: Link is the
universal runtime, not a Bot/Actor helper). **Core built on `origin/main`:** one signed binary, pairing, control
universal runtime, not a Bot/Actor helper). **Core built on `origin/main`:** one signed binary, pairing, control
link, WHIP publisher, plugin runtime, the Adeept and Cozmo plugins, installer and CI. Still to add for its other
link, WHIP publisher, plugin runtime, the Adeept and Cozmo plugins, installer and CI. Still to add for its other
roles: computer control (terminal, files, screen, input, browser) under control levels, local kill switch and
roles: computer control (terminal, files, screen, input, browser) under control levels, local kill switch and
recording (Actor's need, T17); execution-worker capability (Run's need); cache/storage/delivery capability (Media's
recording (Actor's need, T17); execution-worker capability (Run's need); cache/storage/delivery capability (Media's
need, T4). **Local policy always overrides cloud commands.**"*
need, T4). **Local policy always overrides cloud commands.**"*
-- **Products bind capabilities, not device code.** Plan lines 321–324: *"Node identity is a Network principal.
- Network owns the node/device principal and pairing credentials. Services manages the resource; Node presents
- capabilities (T14); Bot binds robot resources to Node capabilities (T15); Actor binds computer control (T17); Run
+- **Products bind capabilities, not device code.** Plan lines 321–324: *"Link identity is a Network principal.
+ Network owns the node/device principal and pairing credentials. Services manages the resource; Link presents
+ capabilities (T14); Bot binds robot resources to Link capabilities (T15); Actor binds computer control (T17); Run
binds execution workers (T14); Media binds cache/storage (T4). Bot keeps robots, robot profiles, operators, command
binds execution workers (T14); Media binds cache/storage (T4). Bot keeps robots, robot profiles, operators, command
leases and physical safety — not universal device identity."*
leases and physical safety — not universal device identity."*
- **What the code already does** (OpenVibe Link, `origin/main`): one Go static binary per platform — pairing
- **What the code already does** (OpenVibe Link, `origin/main`): one Go static binary per platform — pairing
@@ -35,20 +35,20 @@ link.
@@ -35,20 +35,20 @@ link.
- **The capability names are already agreed.** ADR-046 §5 reserves `worker:function`, `worker:code`,
- **The capability names are already agreed.** ADR-046 §5 reserves `worker:function`, `worker:code`,
`worker:browser`, `worker:linux`, `worker:desktop` and `worker:gpu` for the Link job worker, one per
`worker:browser`, `worker:linux`, `worker:desktop` and `worker:gpu` for the Link job worker, one per
`platform.runtime-class@1` class, in `platform.resource-offer@1.$defs.reservedWorkerCapabilities`.
`platform.runtime-class@1` class, in `platform.resource-offer@1.$defs.reservedWorkerCapabilities`.
-- **The control levels are plan wording.** Plan lines 745–747: *"each Node has a control level set on the device
+- **The control levels are plan wording.** Plan lines 745–747: *"each Link has a control level set on the device
(Observe / Ask / Trusted / Full control), a local indicator, a local kill switch, optional container/VM
(Observe / Ask / Trusted / Full control), a local indicator, a local kill switch, optional container/VM
confinement, replayable transcripts and file changes, and local secrets used through handles."* Of that, the local
confinement, replayable transcripts and file changes, and local secrets used through handles."* Of that, the local
kill switch (the persisted stop latch) exists; the four named levels and recording do not.
kill switch (the persisted stop latch) exists; the four named levels and recording do not.
-- **Media's need is stated too.** Plan lines 366–368: *"Node is a storage/delivery candidate too
+- **Media's need is stated too.** Plan lines 366–368: *"Link is a storage/delivery candidate too
(`media.cache`, `storage.hot`, prepaid disk and bandwidth), so the placement engine can use unused capacity on
(`media.cache`, `storage.hot`, prepaid disk and bandwidth), so the placement engine can use unused capacity on
- already-paid OpenVibe nodes before buying CDN traffic."*
+ already-paid OpenVibe Links before buying CDN traffic."*
## Decision
## Decision
-### 1. One Node, the universal runtime
+### 1. One Link, the universal runtime
The Link is OpenVibe's universal runtime — one agent for a person's computers, servers, Raspberry Pis, phones and
The Link is OpenVibe's universal runtime — one agent for a person's computers, servers, Raspberry Pis, phones and
-robots. It is **not** a Bot helper or an Actor helper: Bot, Actor, Run and Media are all clients of the same Node,
+robots. It is **not** a Bot helper or an Actor helper: Bot, Actor, Run and Media are all clients of the same Link,
and each binds the capabilities it needs. ADR-043 remains the robot/device half (robots, robot profiles, operators,
and each binds the capabilities it needs. ADR-043 remains the robot/device half (robots, robot profiles, operators,
command leases and physical safety); this ADR is the runtime half. The one split this record draws inside ADR-043 is
command leases and physical safety); this ADR is the runtime half. The one split this record draws inside ADR-043 is
the credential: **Network owns the node/device principal and issues the pairing credential; Bot keeps the robot
the credential: **Network owns the node/device principal and issues the pairing credential; Bot keeps the robot
@@ -61,7 +61,7 @@ stands.
@@ -61,7 +61,7 @@ stands.
A role is **a capability the Link advertises plus local configuration**, never a second agent or a second install.
A role is **a capability the Link advertises plus local configuration**, never a second agent or a second install.
- The Link is a Network principal (its node/device principal and pairing credential are Network's), and its resource
- The Link is a Network principal (its node/device principal and pairing credential are Network's), and its resource
is named by OVRN (ADR-048). Network owns identity and pairing; Bot keeps the robot binding and the control link
is named by OVRN (ADR-048). Network owns identity and pairing; Bot keeps the robot binding and the control link
- (amending ADR-043's credential paragraph); Node presents capabilities.
+ (amending ADR-043's credential paragraph); Link presents capabilities.
### 3. Execution worker (Run's need)
### 3. Execution worker (Run's need)
@@ -102,7 +102,7 @@ stands.
@@ -102,7 +102,7 @@ stands.
## Alternatives considered
## Alternatives considered
-- **Node as a Bot-only helper (the plan's earlier framing):** rejected by the revision. Bot, Actor, Run and Media
+- **Link as a Bot-only helper (the plan's earlier framing):** rejected by the revision. Bot, Actor, Run and Media
would each grow a device agent, and an owner would install and pair several.
would each grow a device agent, and an owner would install and pair several.
- **A separate agent per product (an Actor agent, a Run worker, a Media cache daemon):** rejected. One binary, one
- **A separate agent per product (an Actor agent, a Run worker, a Media cache daemon):** rejected. One binary, one
control link and one local policy are what make a single device safe to reason about; capabilities distinguish the
control link and one local policy are what make a single device safe to reason about; capabilities distinguish the
@@ -128,7 +128,7 @@ stands.
@@ -128,7 +128,7 @@ stands.
2. **One principal or many.** Whether a Link serving several products/projects is one Network principal with scoped
2. **One principal or many.** Whether a Link serving several products/projects is one Network principal with scoped
grants or one principal per project is Network's decision and is open (ADR-048 OVRN).
grants or one principal per project is Network's decision and is open (ADR-048 OVRN).
3. **Cache/storage contracts.** Which offer contracts carry the Link's cache/storage role — the existing
3. **Cache/storage contracts.** Which offer contracts carry the Link's cache/storage role — the existing
- `platform.storage-offer@1` / `platform.delivery-offer@1` or a Node-specific one — and whether placement policy
+ `platform.storage-offer@1` / `platform.delivery-offer@1` or a Link-specific one — and whether placement policy
stays Media's, is open.
stays Media's, is open.
4. **Other users' workloads.** Whether a Link may run other users' jobs or serve other users' computer control, or
4. **Other users' workloads.** Whether a Link may run other users' jobs or serve other users' computer control, or
only its owner's, mirrors ADR-046's `user-owned` question and is open.
only its owner's, mirrors ADR-046's `user-owned` question and is open.
@@ -140,7 +140,7 @@ stands.
@@ -140,7 +140,7 @@ stands.
- The release binary is not code-signed and has no self-update: releases publish `SHA256SUMS` only.
- The release binary is not code-signed and has no self-update: releases publish `SHA256SUMS` only.
- The four control-level names and the local indicator are plan wording; the code today has a local stop latch, not
- The four control-level names and the local indicator are plan wording; the code today has a local stop latch, not
the four levels.
the four levels.
-- No Actor, Run or Media consumer binds these Node capabilities on `origin/main`; Run is a placeholder service
+- No Actor, Run or Media consumer binds these Link capabilities on `origin/main`; Run is a placeholder service
(ADR-036).
(ADR-036).
- The Link worker is reached over the Bot control link today; it is not yet an offer producer, because the Run that
- The Link worker is reached over the Bot control link today; it is not yet an offer producer, because the Run that
would convert its descriptor is not built.
would convert its descriptor is not built.
@@ -77,7 +77,7 @@ no existing caller's eligible set, and the SDK must not add it to its default li
@@ -77,7 +77,7 @@ no existing caller's eligible set, and the SDK must not add it to its default li
An OpenVibe Link that runs jobs advertises one `worker:<class>` capability in its `resource-offer.capabilities` for
An OpenVibe Link 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`
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
+(submitted code in a sandbox) today, and `worker:browser`, `worker:linux`, `worker:desktop` and `worker:gpu` as Link
gains them. These six names are reserved for the Link job worker (`$defs.reservedWorkerCapabilities` in
gains them. These six names are reserved for the Link job worker (`$defs.reservedWorkerCapabilities` in
`platform.resource-offer@1`); every other `worker:` name (`worker:ffmpeg`, `worker:ai-gpu`) is an ordinary capability.
`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`.
A requirement asks for a class by listing the same name in its `capabilities`.
@@ -18,12 +18,12 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).
@@ -18,12 +18,12 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).
verdict is one path per pillar: build a focused relay on `quic-go` for Share, adopt headscale + `tsnet` with a
verdict is one path per pillar: build a focused relay on `quic-go` for Share, adopt headscale + `tsnet` with a
tenant boundary for Connect, build the policy/abuse layer on top of Cloudflare and Network identity, and extend
tenant boundary for Connect, build the policy/abuse layer on top of Cloudflare and Network identity, and extend
Events for hooks. §3.3 and §3.4 name what to build and what not to reuse.
Events for hooks. §3.3 and §3.4 name what to build and what not to reuse.
-- **The data plane is Go; the control plane is Node.** The Link agent is Go (ADR-045). QUIC, HTTP/3, datagrams,
- half-close and backpressure are native there and awkward in Node.js. The control API can stay Node/Express, like
+- **The data plane is Go; the control plane is Link.** The Link agent is Go (ADR-045). QUIC, HTTP/3, datagrams,
+ half-close and backpressure are native there and awkward in Node.js. The control API can stay Link/Express, like
every other network service. The edge is a separate, exposed, bandwidth-heavy tier and must not share a host with
every other network service. The edge is a separate, exposed, bandwidth-heavy tier and must not share a host with
the control plane (research §4.1).
the control plane (research §4.1).
-- **Node already has a principal.** Network owns node principals (`nod_…`, `ids.newId('node')`) and pairs machines;
- `network.node.revoked` already exists in Network's manifest. Connect must reuse that principal, not mint a second
+- **Link already has a principal.** Network owns node principals (`lnk_…`, `ids.newId('link')`) and pairs machines;
+ `network.link.revoked` already exists in Network's manifest. Connect must reuse that principal, not mint a second
machine identity. Network also owns OIDC, project membership and grants; Domains owns DNS proposals; Events owns
machine identity. Network also owns OIDC, project membership and grants; Domains owns DNS proposals; Events owns
durable delivery and v2 signatures (ADR-042); Report and Billing consume usage.
durable delivery and v2 signatures (ADR-042); Report and Billing consume usage.
- **Space already uses `<name>.openvibe.website`.** A single-label name is in use for tenant sites. A new share cannot
- **Space already uses `<name>.openvibe.website`.** A single-label name is in use for tenant sites. A new share cannot
@@ -46,19 +46,19 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).
@@ -46,19 +46,19 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).
## Decision
## Decision
1. **Share is a focused Go edge relay, `openvibe-edge`, plus a Link `connect` core in OpenVibe Link.** The relay is
1. **Share is a focused Go edge relay, `openvibe-edge`, plus a Link `connect` core in OpenVibe Link.** The relay is
- built on `quic-go` (MIT): one QUIC connection per Node, ALPN `ovconnect/1`, TLS 1.3, streams for HTTP/WebSocket/TCP,
+ built on `quic-go` (MIT): one QUIC connection per Link, ALPN `ovconnect/1`, TLS 1.3, streams for HTTP/WebSocket/TCP,
datagrams for UDP, and an HTTP/2 + WebSocket fallback on 443 for networks that block UDP. It routes by SNI/Host to a
datagrams for UDP, and an HTTP/2 + WebSocket fallback on 443 for networks that block UDP. It routes by SNI/Host to a
`share_id` from Connect's name registry, never from a user-controlled header, checks the signed policy, and then
`share_id` from Connect's name registry, never from a user-controlled header, checks the signed policy, and then
- opens a stream to the Node. It does not own users, shares, policies or audit. No frp, sish, zrok, Pangolin or
+ opens a stream to the Link. It does not own users, shares, policies or audit. No frp, sish, zrok, Pangolin or
cloudflared code is forked or embedded.
cloudflared code is forked or embedded.
-2. **The control API is a new Node service, OpenVibe.Connect.** It owns shares, share policies, devices, mesh policies,
+2. **The control API is a new Link service, OpenVibe.Connect.** It owns shares, share policies, devices, mesh policies,
hooks, the name registry, abuse holds, audit and quota counters in `ov_connect` PostgreSQL; it mints tunnel tokens
hooks, the name registry, abuse holds, audit and quota counters in `ov_connect` PostgreSQL; it mints tunnel tokens
and signs the short-lived policy cache the edge reads. It is a peer of Domains, Events, Network, Report and Billing,
and signs the short-lived policy cache the edge reads. It is a peer of Domains, Events, Network, Report and Billing,
not a module of Bot. Bot keeps robots, the device link and command safety (ADR-043); Connect keeps shares, the mesh,
not a module of Bot. Bot keeps robots, the device link and command safety (ADR-043); Connect keeps shares, the mesh,
policies and hooks.
policies and hooks.
3. **Connect mesh is headscale + embedded `tsnet`, with a tenant boundary that must be proven before consumer
3. **Connect mesh is headscale + embedded `tsnet`, with a tenant boundary that must be proven before consumer
multi-tenancy.** v1 maps one workspace to one headscale tenant: a person's own network for a personal account, a
multi-tenancy.** v1 maps one workspace to one headscale tenant: a person's own network for a personal account, a
- project's network for a team. Node embeds `tsnet` (userspace, no root, no TUN) for the private mesh; the public
+ project's network for a team. Link embeds `tsnet` (userspace, no root, no TUN) for the private mesh; the public
ingress stays Share. headscale does not implement Funnel or Serve and Connect must not promise Funnel-compatible
ingress stays Share. headscale does not implement Funnel or Serve and Connect must not promise Funnel-compatible
behaviour. The tenant-isolation proof must cover: no cross-tenant MagicDNS resolution, no cross-tenant route or
behaviour. The tenant-isolation proof must cover: no cross-tenant MagicDNS resolution, no cross-tenant route or
exit-node leak, no shared pre-auth key, and revocation in one tenant leaving the others untouched. If the proof
exit-node leak, no shared pre-auth key, and revocation in one tenant leaving the others untouched. If the proof
@@ -68,10 +68,10 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).
@@ -68,10 +68,10 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).
is for PCs, NAS and servers. DERP is self-hosted (`derper`) so relay traffic stays on OpenVibe infrastructure;
is for PCs, NAS and servers. DERP is self-hosted (`derper`) so relay traffic stays on OpenVibe infrastructure;
Live's TURN is for WebRTC and is not a WireGuard relay.
Live's TURN is for WebRTC and is not a WireGuard relay.
4. **Identity and the tunnel handshake use two credentials, both checked.** The Link presents a Network
4. **Identity and the tunnel handshake use two credentials, both checked.** The Link presents a Network
- client-credentials token whose subject is its node principal (`node:nod_…`) and whose audience is
+ client-credentials token whose subject is its node principal (`link:lnk_…`) and whose audience is
`openvibe.website`; the request body is `website.share-connect-request@1` with the `share_id`, the same `node_id`
`openvibe.website`; the request body is `website.share-connect-request@1` with the `share_id`, the same `node_id`
and a short-lived, single-use, share-scoped `tunnel_token` minted by Connect. The edge validates the token against
and a short-lived, single-use, share-scoped `tunnel_token` minted by Connect. The edge validates the token against
- Network's JWKS, requires the subject to equal `node:<node_id>` (for example `node:nod_…`), redeems the
+ Network's JWKS, requires the subject to equal `link:<node_id>` (for example `link:lnk_…`), redeems the
tunnel token with Connect exactly once and binds the connection to that share. One credential is never
tunnel token with Connect exactly once and binds the connection to that share. One credential is never
enough. The tunnel token is sent once over TLS and is
enough. The tunnel token is sent once over TLS and is
never stored, logged, audited or published in an event. OIDC at the share host uses Authorization Code + PKCE
never stored, logged, audited or published in an event. OIDC at the share host uses Authorization Code + PKCE
@@ -79,10 +79,10 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).
@@ -79,10 +79,10 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).
`share_id`, `sub` and `policy_version`.
`share_id`, `sub` and `policy_version`.
`POST /api/v1/shares` returns only `website.share@1`; its response has no tunnel token. The Link obtains the
`POST /api/v1/shares` returns only `website.share@1`; its response has no tunnel token. The Link obtains the
short-lived, single-use token through a separate authenticated credential exchange before connecting. CON-5
short-lived, single-use token through a separate authenticated credential exchange before connecting. CON-5
- defines that exchange's endpoint, request and response schemas, authorization, and delivery to the Node.
+ defines that exchange's endpoint, request and response schemas, authorization, and delivery to the Link.
5. **Revocation is immediate and has one owner per trigger.** `DELETE /api/v1/shares/:id` writes
5. **Revocation is immediate and has one owner per trigger.** `DELETE /api/v1/shares/:id` writes
`website.share.revoked`, closes the edge route and tunnel, removes the DNS route if Connect added one and makes the
`website.share.revoked`, closes the edge route and tunnel, removes the DNS route if Connect added one and makes the
- public host return 410 within the 5-second budget. `network.node.revoked` closes every share bound to that node.
+ public host return 410 within the 5-second budget. `network.link.revoked` closes every share bound to that node.
A policy change bumps `policy_version`, so the edge's cached signed policy is stale on the next request. Removing a
A policy change bumps `policy_version`, so the edge's cached signed policy is stale on the next request. Removing a
device revokes its mesh key at the control plane. No capability, route or cached policy survives a revoked share.
device revokes its mesh key at the control plane. No capability, route or cached policy survives a revoked share.
6. **Policy is enforced at the edge, authored and audited by Connect, and fronted by Cloudflare.** Cloudflare
6. **Policy is enforced at the edge, authored and audited by Connect, and fronted by Cloudflare.** Cloudflare
@@ -106,8 +106,8 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).
@@ -106,8 +106,8 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).
Events to retry. Events produces `events.hook.received|delivered|dead`; Connect writes none of them. No smee or
Events to retry. Events produces `events.hook.received|delivered|dead`; Connect writes none of them. No smee or
Hookdeck is adopted.
Hookdeck is adopted.
8. **The SDK stays MIT and thin.** `openvibe-sdk` (MIT) gets at most a thin `openvibe-sdk/connect` client that talks
8. **The SDK stays MIT and thin.** `openvibe-sdk` (MIT) gets at most a thin `openvibe-sdk/connect` client that talks
- to the local Node daemon over HTTP/Unix socket and to the control API over HTTP. The QUIC data plane, the mesh
- (`tsnet`) and the policy engine stay in the AGPL Node/Connect/edge code. No AGPL/GPL tunnel code is copied into the
+ to the local Link daemon over HTTP/Unix socket and to the control API over HTTP. The QUIC data plane, the mesh
+ (`tsnet`) and the policy engine stay in the AGPL Link/Connect/edge code. No AGPL/GPL tunnel code is copied into the
SDK or `openvibe-shared` (MIT). This keeps the permissive client boundary and the copyleft in the agent.
SDK or `openvibe-shared` (MIT). This keeps the permissive client boundary and the copyleft in the agent.
9. **Naming and the first proof use one label.** The first proof is the owner's agent portal at
9. **Naming and the first proof use one label.** The first proof is the owner's agent portal at
`https://agents-<owner>.openvibe.website` (single label), behind OpenVibe sign-in, opened on a phone over 5G with a
`https://agents-<owner>.openvibe.website` (single label), behind OpenVibe sign-in, opened on a phone over 5G with a
@@ -127,9 +127,9 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).
@@ -127,9 +127,9 @@ authority per resource) and ADR-056 (project secrets: write-only, used by name).
| Concern | Owner | Not this service |
| Concern | Owner | Not this service |
|---|---|---|
|---|---|---|
-| Share record, name registry, policy, tunnel token, inspector metadata, audit | OpenVibe.Connect (Node control API, `ov_connect`) | the edge keeps active session state and a signed policy cache only |
+| Share record, name registry, policy, tunnel token, inspector metadata, audit | OpenVibe.Connect (Link control API, `ov_connect`) | the edge keeps active session state and a signed policy cache only |
| Public QUIC/HTTP/WS/SSE/TCP/UDP ingress, routing, edge policy check, rate limits | `openvibe-edge` (Go, `quic-go`) | it does not own users, shares, policies, audit or hooks |
| Public QUIC/HTTP/WS/SSE/TCP/UDP ingress, routing, edge policy check, rate limits | `openvibe-edge` (Go, `quic-go`) | it does not own users, shares, policies, audit or hooks |
-| Node tunnel, local forwarding, `ov share`, `tsnet` | OpenVibe Link (`internal/connect`) | it does not decide policy or open arbitrary hosts |
+| Link tunnel, local forwarding, `ov share`, `tsnet` | OpenVibe Link (`internal/connect`) | it does not decide policy or open arbitrary hosts |
| Account, node principal, OIDC, grants, revocation event | OpenVibe.Network | Connect never mints identity |
| Account, node principal, OIDC, grants, revocation event | OpenVibe.Network | Connect never mints identity |
| DNS proposal, TXT/CNAME, certificate path | OpenVibe.Domains | Connect never writes DNS or holds a provider credential |
| DNS proposal, TXT/CNAME, certificate path | OpenVibe.Domains | Connect never writes DNS or holds a provider credential |
| Durable event store, v2 signature, retries, DLQ, replay | OpenVibe.Events | Connect creates the subscription; the edge does not queue |
| Durable event store, v2 signature, retries, DLQ, replay | OpenVibe.Events | Connect creates the subscription; the edge does not queue |
@@ -212,7 +212,7 @@ capability id is concrete; `website.abuse.review` stays the moderator queue. Non
@@ -212,7 +212,7 @@ capability id is concrete; `website.abuse.review` stays the moderator queue. Non
- `research-2026-10-29/website-connect.md` §1.1–1.7, §3.2–3.4, §4.1–4.13 (checked 2026-10-29).
- `research-2026-10-29/website-connect.md` §1.1–1.7, §3.2–3.4, §4.1–4.13 (checked 2026-10-29).
- `OpenVibe_New_Domains_Plan_2026-10-28.md` §5.9b and the CON-1…CON-21 ledger.
- `OpenVibe_New_Domains_Plan_2026-10-28.md` §5.9b and the CON-1…CON-21 ledger.
-- ADR-053 (Website/Host boundary), ADR-045 (Node runtime), ADR-042 (events fabric), ADR-043 (Bot devices), ADR-048
+- ADR-053 (Website/Host boundary), ADR-045 (Link runtime), ADR-042 (events fabric), ADR-043 (Bot devices), ADR-048
(control plane), ADR-056 (project secrets), ADR-028 (expand/migrate/contract).
(control plane), ADR-056 (project secrets), ADR-028 (expand/migrate/contract).
- `manifests/products/openvibe.website.json` (the distinct product/redirect manifest, preserved), ADR-005 (realtime),
- `manifests/products/openvibe.website.json` (the distinct product/redirect manifest, preserved), ADR-005 (realtime),
`manifests/services/events.json` and `manifests/services/network.json` (node revocation and hook events).
`manifests/services/events.json` and `manifests/services/network.json` (node revocation and hook events).
@@ -39,11 +39,11 @@
@@ -39,11 +39,11 @@
bytes that were scanned, and its scan findings may name the `tool` they are about. `actor.skill@1` and
bytes that were scanned, and its scan findings may name the `tool` they are about. `actor.skill@1` and
`actor.skill-listing@1` are unchanged.
`actor.skill-listing@1` are unchanged.
3. **The Link runner** (`[email protected]`, a declared function like `[email protected]`):
3. **The Link runner** (`[email protected]`, a declared function like `[email protected]`):
- - off unless the owner turns it on (`worker.functions`, `openvibe node skills on`);
+ - off unless the owner turns it on (`worker.functions`, `openvibe-link skills on`);
- takes `{ tool, entry, input }` and the code as job `inputs` (Media objects pinned by sha256; a mismatch ends the
- takes `{ tool, entry, input }` and the code as job `inputs` (Media objects pinned by sha256; a mismatch ends the
job before anything runs);
job before anything runs);
- runs `node --permission --allow-fs-read=<the job's input dir>` with no child process, no worker, no addons and
- runs `node --permission --allow-fs-read=<the job's input dir>` with no child process, no worker, no addons and
- `--disallow-code-generation-from-strings`, inside the function sandbox Node already has (its own root, limits,
+ `--disallow-code-generation-from-strings`, inside the function sandbox Link already has (its own root, limits,
no credential, network per `net`);
no credential, network per `net`);
- the input arrives on stdin as JSON; the tool writes one JSON value (≤ 64 KiB; more fails the job `limit`);
- the input arrives on stdin as JSON; the tool writes one JSON value (≤ 64 KiB; more fails the job `limit`);
stderr is kept for the person (≤ 16 KiB).
stderr is kept for the person (≤ 16 KiB).
@@ -74,7 +74,7 @@
@@ -74,7 +74,7 @@
- **Run the code on Actor's server.** Rejected: Actor holds every person's agent, token and registry record; one
- **Run the code on Actor's server.** Rejected: Actor holds every person's agent, token and registry record; one
skill's mistake would be a registry-wide incident, and the design rule forbids it outright.
skill's mistake would be a registry-wide incident, and the design rule forbids it outright.
- **A container on the host that publishes the skill.** Rejected: the host is someone's machine, and a container
- **A container on the host that publishes the skill.** Rejected: the host is someone's machine, and a container
- boundary we do not control is not a sandbox promise we can make to every site. Node already has the sandbox, the
+ boundary we do not control is not a sandbox promise we can make to every site. Link already has the sandbox, the
limits, the egress policy and the placement; running there needs no second runtime.
limits, the egress policy and the placement; running there needs no second runtime.
- **Python and npm in v1.** Deferred: both widen the runtime surface (packages, install recipes, dependency
- **Python and npm in v1.** Deferred: both widen the runtime surface (packages, install recipes, dependency
resolution) before the scan and the sandbox have a release behind them. Each needs its own review, as does
resolution) before the scan and the sandbox have a release behind them. Each needs its own review, as does
@@ -82,7 +82,7 @@
@@ -82,7 +82,7 @@
## Migration and rollback
## Migration and rollback
-Both new contracts are additive majors; nothing parses `actor.skill@2` until Actor's registry and Node's runner
+Both new contracts are additive majors; nothing parses `actor.skill@2` until Actor's registry and Link's runner
ship, and @1 keeps working for every existing prompt skill. The only data a sandbox listing adds is the Media
ship, and @1 keeps working for every existing prompt skill. The only data a sandbox listing adds is the Media
objects that hold the code; they are private to Actor's tenant and can be deleted with the listing. Rollback is
objects that hold the code; they are private to Actor's tenant and can be deleted with the listing. Rollback is
retiring the @2 listing and turning the runner off: no prompt skill is affected, no job can be submitted without
retiring the @2 listing and turning the runner off: no prompt skill is affected, no job can be submitted without
@@ -103,5 +103,5 @@ the `skill.run` kind, and nothing ever ran on our servers, so there is no server
@@ -103,5 +103,5 @@ the `skill.run` kind, and nothing ever ran on our servers, so there is no server
`secret`, `token`, `env` or `key`;
`secret`, `token`, `env` or `key`;
- a @2 listing pins each code resource's Media id and sha256, the pinned sha256 is the document's own bytes, and a
- a @2 listing pins each code resource's Media id and sha256, the pinned sha256 is the document's own bytes, and a
sandbox listing without code is invalid.
sandbox listing without code is invalid.
-Node's own tests (in OpenVibe Link) prove the runner: a tool cannot read env, spawn, write a file, open a socket
+Link's own tests (in OpenVibe Link) prove the runner: a tool cannot read env, spawn, write a file, open a socket
under `deny`, or exceed its output.
under `deny`, or exceed its output.
@@ -42,9 +42,9 @@ Platform-wide decisions from the OpenVibe development roadmap (section 10). Each
@@ -42,9 +42,9 @@ Platform-wide decisions from the OpenVibe development roadmap (section 10). Each
| [ADR-036](ADR-036-run-execution-authority.md) | Run is an execution authority, not a Host detail: Host deploys persistent applications, Run executes isolated jobs/environments (`function`, `code`, `browser`, `linux`, `desktop`, `gpu`), placed by the Fabric on the cheapest capable offer; provider adapters are offers, not code paths; per-second metering, TTL, idle suspend, cancel, snapshot/resume, stdout streaming, artifacts via Media, limits, network policy and strong isolation | Proposed 2026-10-05 (plan T14; plan §8 lists it to write before its code, and Run's code is owner-blocked), for the owner's review |
| [ADR-036](ADR-036-run-execution-authority.md) | Run is an execution authority, not a Host detail: Host deploys persistent applications, Run executes isolated jobs/environments (`function`, `code`, `browser`, `linux`, `desktop`, `gpu`), placed by the Fabric on the cheapest capable offer; provider adapters are offers, not code paths; per-second metering, TTL, idle suspend, cancel, snapshot/resume, stdout streaming, artifacts via Media, limits, network policy and strong isolation | Proposed 2026-10-05 (plan T14; plan §8 lists it to write before its code, and Run's code is owner-blocked), for the owner's review |
| [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-043](ADR-043-bot-devices-and-control.md) | Bot — devices, pairing, control and safety: robots, devices and credentials are separate (one hashed, rotatable, revocable device credential); pairing is a one-time code; one outbound WebSocket per device with absolute deadlines, device-side stops and a latched e-stop; robot profiles rather than rows; owner/operators/viewers with a timed public queue; everything audited; OpenVibe.Bot is its own service | Accepted 2026-09-29 (plan track T15, "Bot complete"; owner direction the same evening: openvibe.bot is an open control panel for robots, streaming optional; the first robots are the owner's Adeept 4WD Smart Car for Raspberry Pi and a Cozmo-based robot) |
| [ADR-043](ADR-043-bot-devices-and-control.md) | Bot — devices, pairing, control and safety: robots, devices and credentials are separate (one hashed, rotatable, revocable device credential); pairing is a one-time code; one outbound WebSocket per device with absolute deadlines, device-side stops and a latched e-stop; robot profiles rather than rows; owner/operators/viewers with a timed public queue; everything audited; OpenVibe.Bot is its own service | Accepted 2026-09-29 (plan track T15, "Bot complete"; owner direction the same evening: openvibe.bot is an open control panel for robots, streaming optional; the first robots are the owner's Adeept 4WD Smart Car for Raspberry Pi and a Cozmo-based robot) |
-| [ADR-044](ADR-044-actor-runtime.md) | The Actor runtime: one `platform.task@1` task (`POST /v1/tasks`) with a mode, a per-task and per-day budget, result, cost and explanation (a chain of `platform.placement-result@1`); adapters declare capabilities, limits, a reviewed rate card, a trust class and BYO keys; modes are `openvibe-sdk/placement` objectives; live signals auto-balance with hysteresis and immediate failover; a `verifying` state holds cheap-first cross-family checks and the task's own tests; the five ADR-046 trust classes, `private` narrowing to OpenVibe-only, Node control levels underneath; progress over SSE, cancel and webhooks; owner `network` (no Actor service manifest exists yet) | Proposed 2026-10-05 (plan T17), for the owner's review |
+| [ADR-044](ADR-044-actor-runtime.md) | The Actor runtime: one `platform.task@1` task (`POST /v1/tasks`) with a mode, a per-task and per-day budget, result, cost and explanation (a chain of `platform.placement-result@1`); adapters declare capabilities, limits, a reviewed rate card, a trust class and BYO keys; modes are `openvibe-sdk/placement` objectives; live signals auto-balance with hysteresis and immediate failover; a `verifying` state holds cheap-first cross-family checks and the task's own tests; the five ADR-046 trust classes, `private` narrowing to OpenVibe-only, Link control levels underneath; progress over SSE, cancel and webhooks; owner `network` (no Actor service manifest exists yet) | Proposed 2026-10-05 (plan T17), for the owner's review |
| [ADR-045](ADR-045-node-universal-runtime.md) | The Link is OpenVibe's universal runtime, not a Bot/Actor helper: one checksummed release binary and one control link serving the execution worker (`worker:<class>`, per ADR-046 §5), computer control under device-set control levels with a local kill switch and recording, and cache/storage/delivery; local policy always overrides cloud commands; ADR-043 stays the robot/device half | Proposed 2026-10-05 (plan T14 and §8 revision; consistent with ADR-046 and ADR-048, amends ADR-043's credential paragraph), for the owner's review |
| [ADR-045](ADR-045-node-universal-runtime.md) | The Link is OpenVibe's universal runtime, not a Bot/Actor helper: one checksummed release binary and one control link serving the execution worker (`worker:<class>`, per ADR-046 §5), computer control under device-set control levels with a local kill switch and recording, and cache/storage/delivery; local policy always overrides cloud commands; ADR-043 stays the robot/device half | Proposed 2026-10-05 (plan T14 and §8 revision; consistent with ADR-046 and ADR-048, amends ADR-043's credential paragraph), for the owner's review |
-| [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 |
+| [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>` Link 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 |
| [ADR-048](ADR-048-services-control-plane.md) | The Services control plane: Services aggregates authorities and owns no other service's rows; every console action is a `common.resource-control-request@1` to the owning authority, answered with `common.resource-control-result@1` (idempotency keys, `confirmation_id` for sensitive actions); OVRN `ovrn:<service>:<project_id>:<type>/<id>` (`common.resource-name@1`) is the one cross-service resource name | Accepted 2026-10-04 (plan T13) |
| [ADR-048](ADR-048-services-control-plane.md) | The Services control plane: Services aggregates authorities and owns no other service's rows; every console action is a `common.resource-control-request@1` to the owning authority, answered with `common.resource-control-result@1` (idempotency keys, `confirmation_id` for sensitive actions); OVRN `ovrn:<service>:<project_id>:<type>/<id>` (`common.resource-name@1`) is the one cross-service resource name | Accepted 2026-10-04 (plan T13) |
| [ADR-053](ADR-053-website-host-boundary.md) | The Website/Host boundary: OpenVibe.Website owns authoring (projects, pages, revisions, builds, Media asset pointers and intended domains) and OpenVibe.Host owns site, deploy, activation, domain binding/verification/TLS and serving; Website keeps no duplicate `host_sites`/`host_deploys` rows and calls Host's routes; a Website build is not a Host deploy; Website composes Host + Codes + Actor + Run + Media and is not OpenVibe.Sites | Proposed 2026-10-05 (plan T19), for the owner's review |
| [ADR-053](ADR-053-website-host-boundary.md) | The Website/Host boundary: OpenVibe.Website owns authoring (projects, pages, revisions, builds, Media asset pointers and intended domains) and OpenVibe.Host owns site, deploy, activation, domain binding/verification/TLS and serving; Website keeps no duplicate `host_sites`/`host_deploys` rows and calls Host's routes; a Website build is not a Host deploy; Website composes Host + Codes + Actor + Run + Media and is not OpenVibe.Sites | Proposed 2026-10-05 (plan T19), for the owner's review |
| [ADR-054](ADR-054-inventory-authority.md) | The OpenVibe inventory: a new authority, OpenVibe.Inventory (`inventory`, `ov_inventory`), owns item kinds (contract + renderer per surface), definitions (`itd_`), instances (`inv_`), the equipped set and an append-only ledger; issuers grant within their namespace; no selling, buying, trading or conversion until a later money ADR (ADR-012/ADR-025); Live's cosmetics are the first kinds, migrated convert-verify-delete | Accepted 2026-10-09 (plan T21) |
| [ADR-054](ADR-054-inventory-authority.md) | The OpenVibe inventory: a new authority, OpenVibe.Inventory (`inventory`, `ov_inventory`), owns item kinds (contract + renderer per surface), definitions (`itd_`), instances (`inv_`), the equipped set and an append-only ledger; issuers grant within their namespace; no selling, buying, trading or conversion until a later money ADR (ADR-012/ADR-025); Live's cosmetics are the first kinds, migrated convert-verify-delete | Accepted 2026-10-09 (plan T21) |
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
# Locker: end-to-end encryption for anything on OpenVibe
# Locker: end-to-end encryption for anything on OpenVibe
`locker.envelope@1` (`contracts/locker/envelope.v1.json`) is the format. The library that reads and writes it is
`locker.envelope@1` (`contracts/locker/envelope.v1.json`) is the format. The library that reads and writes it is
-`openvibe-sdk/locker` (Node and the browser); `openvibe-sdk/locker/server` checks an envelope's header without
+`openvibe-sdk/locker` (Link and the browser); `openvibe-sdk/locker/server` checks an envelope's header without
decrypting it.
decrypting it.
Locker sits on top of sharing: sharing decides **who may fetch** a thing, Locker decides **who can read** it. With a
Locker sits on top of sharing: sharing decides **who may fetch** a thing, Locker decides **who can read** it. With a
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "provider": "link",
+ "machine": "anyone",
+ "models": {
+ "chat": "llama3.1:8b"
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "provider": "link",
+ "machine": "dev_01M5YGM39VC2RRGDQPRD6D4SHD",
+ "api_key": "sk-not-for-a-machine"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "provider": "link",
+ "models": {
+ "chat": "llama3.1:8b"
+ }
+}
deleted file mode 100644
deleted file mode 100644
@@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
-{
- "provider": "node",
- "machine": "anyone",
- "models": {
- "chat": "llama3.1:8b"
- }
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
-{
- "provider": "node",
- "machine": "dev_01M5YGM39VC2RRGDQPRD6D4SHD",
- "api_key": "sk-not-for-a-machine"
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,6 +0,0 @@
@@ -1,6 +0,0 @@
-{
- "provider": "node",
- "models": {
- "chat": "llama3.1:8b"
- }
-}
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "provider": "link",
+ "machine": "any",
+ "models": {
+ "chat": "llama3.1:8b"
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "provider": "link",
+ "machine": "dev_01M5YGM39VC2RRGDQPRD6D4SHD",
+ "models": {
+ "chat": "llama3.1:8b"
+ }
+}
deleted file mode 100644
deleted file mode 100644
@@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
-{
- "provider": "node",
- "machine": "any",
- "models": {
- "chat": "llama3.1:8b"
- }
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
-{
- "provider": "node",
- "machine": "dev_01M5YGM39VC2RRGDQPRD6D4SHD",
- "models": {
- "chat": "llama3.1:8b"
- }
-}
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+{
+ "subject": "usr_01M4W5JBFEG3QG9ER8W1A2K0RX",
+ "owner": "chat",
+ "provider": "link",
+ "machine": "dev_01M5YGM39VC2RRGDQPRD6D4SHD",
+ "base_url": null,
+ "models": {
+ "chat": "llama3.1:8b"
+ },
+ "budget_usd_per_day": null,
+ "spent_usd_today": 0,
+ "created_at": "2026-10-27T13:00:00.000Z",
+ "updated_at": "2026-10-27T13:00:00.000Z",
+ "last_used_at": null
+}
deleted file mode 100644
deleted file mode 100644
@@ -1,15 +0,0 @@
@@ -1,15 +0,0 @@
-{
- "subject": "usr_01M4W5JBFEG3QG9ER8W1A2K0RX",
- "owner": "chat",
- "provider": "node",
- "machine": "dev_01M5YGM39VC2RRGDQPRD6D4SHD",
- "base_url": null,
- "models": {
- "chat": "llama3.1:8b"
- },
- "budget_usd_per_day": null,
- "spent_usd_today": 0,
- "created_at": "2026-10-27T13:00:00.000Z",
- "updated_at": "2026-10-27T13:00:00.000Z",
- "last_used_at": null
-}
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
{
{
"id": "mem_01JZ0000000000000000000001",
"id": "mem_01JZ0000000000000000000001",
"owner": "user:usr_01JZ0000000000000000000AAA",
"owner": "user:usr_01JZ0000000000000000000AAA",
- "scope": "project:openvibe-node",
+ "scope": "project:openvibe-link",
"kind": "project_rule",
"kind": "project_rule",
"text": "Run the Go tests with go test ./... before a commit.",
"text": "Run the Go tests with go test ./... before a commit.",
"source": "claude-code",
"source": "claude-code",
@@ -3,7 +3,7 @@
@@ -3,7 +3,7 @@
{
{
"text": "Use the repo's formatter before a commit.",
"text": "Use the repo's formatter before a commit.",
"kind": "procedure",
"kind": "procedure",
- "scope": "project:openvibe-node",
+ "scope": "project:openvibe-link",
"source": "claude-code",
"source": "claude-code",
"source_ref": {
"source_ref": {
"path": ".claude/CLAUDE.md",
"path": ".claude/CLAUDE.md",
@@ -19,6 +19,6 @@
@@ -19,6 +19,6 @@
"pairing": {
"pairing": {
"code": "ABCD-EFGH",
"code": "ABCD-EFGH",
"expires_at": "2026-09-29T19:20:00.000Z",
"expires_at": "2026-09-29T19:20:00.000Z",
- "installer": "curl -fsSL https://openvibe.bot/install.sh | sh -s ABCD-EFGH"
+ "installer": "curl -fsSL https://openvibe.link/install.sh | sh -s ABCD-EFGH"
}
}
}
}
@@ -9,7 +9,7 @@
@@ -9,7 +9,7 @@
"type": "user",
"type": "user",
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "-ab",
"name": "-ab",
"status": "online",
"status": "online",
"os": "linux",
"os": "linux",
@@ -9,7 +9,7 @@
@@ -9,7 +9,7 @@
"type": "user",
"type": "user",
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"status": "online",
"status": "online",
"os": "linux",
"os": "linux",
@@ -9,7 +9,7 @@
@@ -9,7 +9,7 @@
"type": "user",
"type": "user",
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "ab-",
"name": "ab-",
"status": "online",
"status": "online",
"os": "linux",
"os": "linux",
@@ -9,7 +9,7 @@
@@ -9,7 +9,7 @@
"type": "user",
"type": "user",
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "a-------------------------------------------------------------z",
"name": "a-------------------------------------------------------------z",
"status": "online",
"status": "online",
"os": "linux",
"os": "linux",
@@ -9,7 +9,7 @@
@@ -9,7 +9,7 @@
"type": "user",
"type": "user",
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "a",
"name": "a",
"status": "online",
"status": "online",
"os": "linux",
"os": "linux",
@@ -9,7 +9,7 @@
@@ -9,7 +9,7 @@
"type": "user",
"type": "user",
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "ab",
"name": "ab",
"status": "online",
"status": "online",
"os": "linux",
"os": "linux",
@@ -9,7 +9,7 @@
@@ -9,7 +9,7 @@
"type": "user",
"type": "user",
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "workstation",
"name": "workstation",
"status": "online",
"status": "online",
"os": "linux",
"os": "linux",
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"iss": "https://openvibe.network",
"iss": "https://openvibe.network",
- "sub": "node:nod_42",
- "actor_type": "node",
+ "sub": "link:lnk_42",
+ "actor_type": "link",
"aud": [
"aud": [
"openvibe.bot"
"openvibe.bot"
],
],
"cap": [
"cap": [
- "network.node.self.manage"
+ "network.link.self.manage"
],
],
"iat": 1790000000,
"iat": 1790000000,
"exp": 1790000300,
"exp": 1790000300,
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"iss": "https://openvibe.network",
"iss": "https://openvibe.network",
- "sub": "node:nod_01JAB2C3D4E5F6G7H8J9K0MNPQ",
- "actor_type": "node",
+ "sub": "link:lnk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "actor_type": "link",
"aud": [
"aud": [
"openvibe.bot"
"openvibe.bot"
],
],
"cap": [
"cap": [
- "network.node.self.manage"
+ "network.link.self.manage"
],
],
"iat": 1790000000,
"iat": 1790000000,
"exp": 1790000300,
"exp": 1790000300,
new file mode 100644
new file mode 100644
@@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
+{
+ "sources": [
+ {
+ "kind": "invalid",
+ "name": "Unknown",
+ "status": "ready",
+ "reason": null
+ }
+ ],
+ "candidates": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
+{
+ "sources": [
+ {
+ "kind": "personal",
+ "name": "Your Link machines",
+ "status": "ready",
+ "reason": null
+ },
+ {
+ "kind": "cloud",
+ "name": "OpenVibe cloud machines",
+ "status": "coming_soon",
+ "reason": "No cloud pool is accepting jobs yet."
+ },
+ {
+ "kind": "exchange",
+ "name": "Exchange GPU rentals",
+ "status": "coming_soon",
+ "reason": "A live quote is required.",
+ "price": null
+ },
+ {
+ "kind": "community",
+ "name": "Community machines",
+ "status": "coming_soon",
+ "reason": "Shared placement is being connected."
+ }
+ ],
+ "candidates": [
+ {
+ "id": "dev_01J0000000000000000000000",
+ "kind": "personal",
+ "name": "Studio",
+ "status": "ready",
+ "reason": null,
+ "price": {
+ "amount_usd": 0,
+ "unit": "hour"
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ], "generated_at": "2026-09-28T12:00:05Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "source": "openvibe-oregon", "nodes": [] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "source": "openvibe-oregon", "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ] }
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "node_id": "node-1",
+ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },
+ "at": "2026-10-04T18:00:00.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",
+ "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },
+ "at": "2026-10-04T18:00:00.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",
+ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ" },
+ "at": "2026-10-04T18:00:00.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnpt",
+ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPT",
+ "owner": { "kind": "project", "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ" },
+ "at": "2026-10-04T18:00:00.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",
+ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },
+ "at": "2026-10-04T18:00:00.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",
+ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },
+ "reason": "the owner disconnected the machine from Bot",
+ "at": "2026-10-04T18:00:00.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
+{
+ "id": "oregon-1",
+ "name": "Oregon 1",
+ "roles": [
+ "web",
+ "app",
+ "data",
+ "ingest"
+ ],
+ "location": {
+ "region": "us-west",
+ "country": "US",
+ "city": "Hillsboro",
+ "lat": 45.52,
+ "lon": -122.99
+ },
+ "provider": "ovh",
+ "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon",
+ "health": {
+ "status": "up",
+ "checked_at": "2026-09-28T12:00:00Z"
+ },
+ "updated_at": "2026-09-28T12:00:00Z",
+ "cell": "WNAM_1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "usa", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "http://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "address": "15.204.79.215", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": "oregon-1", "name": "Oregon 1", "roles": ["mainframe", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": "probe-fra", "roles": ["edge-probe", "edge-relay"], "location": { "region": "eu-central", "country": "DE" }, "health": { "status": "unknown", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }
new file mode 100644
new file mode 100644
@@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
+{
+ "id": "oregon-1",
+ "name": "Oregon 1",
+ "roles": [
+ "web",
+ "app",
+ "data",
+ "ingest"
+ ],
+ "location": {
+ "region": "us-west",
+ "country": "US",
+ "city": "Hillsboro",
+ "lat": 45.52,
+ "lon": -122.99
+ },
+ "provider": "ovh",
+ "beacon": "https://openvibe.network/api/v1/links/oregon-1/beacon",
+ "health": {
+ "status": "up",
+ "checked_at": "2026-09-28T12:00:00Z"
+ },
+ "updated_at": "2026-09-28T12:00:00Z",
+ "cell": "wnam-1"
+}
@@ -2,5 +2,5 @@
@@ -2,5 +2,5 @@
"code": "ABCD-1234",
"code": "ABCD-1234",
"pairing_id": "pair_01JAB2C3D4E5F6G7H8J9K0MNPS",
"pairing_id": "pair_01JAB2C3D4E5F6G7H8J9K0MNPS",
"expires_at": "2026-10-31T09:00:00.000Z",
"expires_at": "2026-10-31T09:00:00.000Z",
- "installer": "curl -fsSL https://openvibe.bot/install | sh -s -- --network https://openvibe.network --pairing pair_01JAB2C3D4E5F6G7H8J9K0MNPS --code ABCD-1234"
+ "installer": "curl -fsSL https://openvibe.link/install | sh -s -- --network https://openvibe.network --pairing pair_01JAB2C3D4E5F6G7H8J9K0MNPS --code ABCD-1234"
}
}
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",
"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",
- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",
"owner": "usr_x",
"owner": "usr_x",
"at": "2026-10-31T09:00:00.000Z"
"at": "2026-10-31T09:00:00.000Z"
}
}
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",
"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",
- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",
"owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
"owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
"at": "2026-10-31T09:00:00.000Z"
"at": "2026-10-31T09:00:00.000Z"
}
}
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"machine_id": "dev_x",
"machine_id": "dev_x",
- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",
"owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
"owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
"at": "2026-10-31T09:00:00.000Z"
"at": "2026-10-31T09:00:00.000Z"
}
}
@@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
{
{
"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",
"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",
- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",
"owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"
"owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS"
}
}
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",
"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",
- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",
"owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
"owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
"at": "2026-10-31T09:00:00.000Z"
"at": "2026-10-31T09:00:00.000Z"
}
}
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",
"node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",
- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",
"owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
"owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
"at": "2026-10-31T09:00:00.000Z"
"at": "2026-10-31T09:00:00.000Z"
}
}
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",
"machine_id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",
- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",
"owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
"owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPS",
"reason": "the owner removed it",
"reason": "the owner removed it",
"at": "2026-10-31T09:00:00.000Z"
"at": "2026-10-31T09:00:00.000Z"
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",
"id": "dev_01JAB2C3D4E5F6G7H8J9K0MNPS",
- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "principal_id": "lnk_01JAB2C3D4E5F6G7H8J9K0MNPS",
"name": "Kitchen server",
"name": "Kitchen server",
"kind": "server",
"kind": "server",
"os": "linux",
"os": "linux",
deleted file mode 100644
deleted file mode 100644
@@ -1 +0,0 @@
@@ -1 +0,0 @@
-{ "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ] }
deleted file mode 100644
deleted file mode 100644
@@ -1 +0,0 @@
@@ -1 +0,0 @@
-{ "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ], "generated_at": "2026-09-28T12:00:05Z" }
deleted file mode 100644
deleted file mode 100644
@@ -1 +0,0 @@
@@ -1 +0,0 @@
-{ "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ] }
deleted file mode 100644
deleted file mode 100644
@@ -1 +0,0 @@
@@ -1 +0,0 @@
-{ "source": "openvibe-oregon", "nodes": [] }
deleted file mode 100644
deleted file mode 100644
@@ -1 +0,0 @@
@@ -1 +0,0 @@
-{ "source": "openvibe-oregon", "nodes": [ { "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" } ] }
deleted file mode 100644
deleted file mode 100644
@@ -1,6 +0,0 @@
@@ -1,6 +0,0 @@
-{
- "node_id": "node-1",
- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",
- "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },
- "at": "2026-10-04T18:00:00.000Z"
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
-{
- "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",
- "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },
- "at": "2026-10-04T18:00:00.000Z"
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,6 +0,0 @@
@@ -1,6 +0,0 @@
-{
- "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",
- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",
- "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR", "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ" },
- "at": "2026-10-04T18:00:00.000Z"
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,6 +0,0 @@
@@ -1,6 +0,0 @@
-{
- "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnpt",
- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPT",
- "owner": { "kind": "project", "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ" },
- "at": "2026-10-04T18:00:00.000Z"
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,6 +0,0 @@
@@ -1,6 +0,0 @@
-{
- "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",
- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",
- "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },
- "at": "2026-10-04T18:00:00.000Z"
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
-{
- "node_id": "n-01jab2c3d4e5f6g7h8j9k0mnps",
- "principal_id": "nod_01JAB2C3D4E5F6G7H8J9K0MNPS",
- "owner": { "kind": "user", "subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR" },
- "reason": "the owner disconnected the machine from Bot",
- "at": "2026-10-04T18:00:00.000Z"
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,25 +0,0 @@
@@ -1,25 +0,0 @@
-{
- "id": "oregon-1",
- "name": "Oregon 1",
- "roles": [
- "web",
- "app",
- "data",
- "ingest"
- ],
- "location": {
- "region": "us-west",
- "country": "US",
- "city": "Hillsboro",
- "lat": 45.52,
- "lon": -122.99
- },
- "provider": "ovh",
- "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon",
- "health": {
- "status": "up",
- "checked_at": "2026-09-28T12:00:00Z"
- },
- "updated_at": "2026-09-28T12:00:00Z",
- "cell": "WNAM_1"
-}
deleted file mode 100644
deleted file mode 100644
@@ -1 +0,0 @@
@@ -1 +0,0 @@
-{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "usa", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }
deleted file mode 100644
deleted file mode 100644
@@ -1 +0,0 @@
@@ -1 +0,0 @@
-{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "http://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }
deleted file mode 100644
deleted file mode 100644
@@ -1 +0,0 @@
@@ -1 +0,0 @@
-{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "address": "15.204.79.215", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }
deleted file mode 100644
deleted file mode 100644
@@ -1 +0,0 @@
@@ -1 +0,0 @@
-{ "id": "oregon-1", "name": "Oregon 1", "roles": ["mainframe", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }
deleted file mode 100644
deleted file mode 100644
@@ -1 +0,0 @@
@@ -1 +0,0 @@
-{ "id": "probe-fra", "roles": ["edge-probe", "edge-relay"], "location": { "region": "eu-central", "country": "DE" }, "health": { "status": "unknown", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }
deleted file mode 100644
deleted file mode 100644
@@ -1 +0,0 @@
@@ -1 +0,0 @@
-{ "id": "oregon-1", "name": "Oregon 1", "roles": ["web", "app", "data", "ingest"], "location": { "region": "us-west", "country": "US", "city": "Hillsboro", "lat": 45.52, "lon": -122.99 }, "provider": "ovh", "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon", "health": { "status": "up", "checked_at": "2026-09-28T12:00:00Z" }, "updated_at": "2026-09-28T12:00:00Z" }
deleted file mode 100644
deleted file mode 100644
@@ -1,25 +0,0 @@
@@ -1,25 +0,0 @@
-{
- "id": "oregon-1",
- "name": "Oregon 1",
- "roles": [
- "web",
- "app",
- "data",
- "ingest"
- ],
- "location": {
- "region": "us-west",
- "country": "US",
- "city": "Hillsboro",
- "lat": 45.52,
- "lon": -122.99
- },
- "provider": "ovh",
- "beacon": "https://openvibe.network/api/v1/nodes/oregon-1/beacon",
- "health": {
- "status": "up",
- "checked_at": "2026-09-28T12:00:00Z"
- },
- "updated_at": "2026-09-28T12:00:00Z",
- "cell": "wnam-1"
-}
new file mode 100644
new file mode 100644
@@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
+{
+ "node_id": "node-1",
+ "cpu": {
+ "cores": 8,
+ "threads": 16
+ },
+ "arch": "x86_64",
+ "memory_mb": 16384,
+ "gpu": {
+ "model": "L4",
+ "count": 1,
+ "vram_mb": 24576
+ },
+ "storage": {
+ "capacity_gb": 500,
+ "available_gb": 300,
+ "kind": "ssd"
+ },
+ "network": {
+ "ingress_mbps": 1000,
+ "egress_mbps": 1000
+ },
+ "regions": [
+ "us-west"
+ ],
+ "tags": [
+ "gpu",
+ "edge"
+ ],
+ "costs": {
+ "per_hour_usd": 0.2,
+ "egress_per_gb_usd": 0.01
+ },
+ "capabilities": [
+ "ffmpeg"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
+{
+ "cpu": {
+ "cores": 8,
+ "threads": 16
+ },
+ "arch": "x86_64",
+ "memory_mb": 16384,
+ "gpu": {
+ "model": "L4",
+ "count": 1,
+ "vram_mb": 24576
+ },
+ "storage": {
+ "capacity_gb": 500,
+ "available_gb": 300,
+ "kind": "ssd"
+ },
+ "network": {
+ "ingress_mbps": 1000,
+ "egress_mbps": 1000
+ },
+ "regions": [
+ "us-west"
+ ],
+ "tags": [
+ "gpu",
+ "edge"
+ ],
+ "costs": {
+ "per_hour_usd": 0.2,
+ "egress_per_gb_usd": 0.01
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
+{
+ "node_id": "node-1",
+ "cpu": {
+ "cores": 8,
+ "threads": 16
+ },
+ "arch": "x86_64",
+ "memory_mb": 16384,
+ "gpu": {
+ "model": "L4",
+ "count": 1,
+ "vram_mb": 24576
+ },
+ "storage": {
+ "capacity_gb": 500,
+ "available_gb": 300,
+ "kind": "ssd"
+ },
+ "network": {
+ "ingress_mbps": 1000,
+ "egress_mbps": 1000
+ },
+ "regions": [
+ "us-west"
+ ],
+ "tags": [
+ "gpu",
+ "edge"
+ ],
+ "costs": {
+ "per_hour_usd": 0.2,
+ "egress_per_gb_usd": 0.01
+ },
+ "capabilities": [
+ "node:http",
+ "worker:ffmpeg"
+ ],
+ "agent_version": "0.9.0",
+ "updated_at": "2026-10-02T00:00:00Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
+{
+ "node_id": "node-1",
+ "cpu": {
+ "cores": 8,
+ "threads": 16
+ },
+ "arch": "x86_64",
+ "memory_mb": 16384,
+ "gpu": {
+ "model": "L4",
+ "count": 1,
+ "vram_mb": 24576
+ },
+ "storage": {
+ "capacity_gb": 500,
+ "available_gb": 300,
+ "kind": "ssd"
+ },
+ "network": {
+ "ingress_mbps": 1000,
+ "egress_mbps": 1000
+ },
+ "regions": [
+ "us-west"
+ ],
+ "tags": [
+ "gpu",
+ "edge"
+ ],
+ "costs": {
+ "per_hour_usd": 0.2,
+ "egress_per_gb_usd": 0.01
+ }
+}
deleted file mode 100644
deleted file mode 100644
@@ -1,37 +0,0 @@
@@ -1,37 +0,0 @@
-{
- "node_id": "node-1",
- "cpu": {
- "cores": 8,
- "threads": 16
- },
- "arch": "x86_64",
- "memory_mb": 16384,
- "gpu": {
- "model": "L4",
- "count": 1,
- "vram_mb": 24576
- },
- "storage": {
- "capacity_gb": 500,
- "available_gb": 300,
- "kind": "ssd"
- },
- "network": {
- "ingress_mbps": 1000,
- "egress_mbps": 1000
- },
- "regions": [
- "us-west"
- ],
- "tags": [
- "gpu",
- "edge"
- ],
- "costs": {
- "per_hour_usd": 0.2,
- "egress_per_gb_usd": 0.01
- },
- "capabilities": [
- "ffmpeg"
- ]
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,33 +0,0 @@
@@ -1,33 +0,0 @@
-{
- "cpu": {
- "cores": 8,
- "threads": 16
- },
- "arch": "x86_64",
- "memory_mb": 16384,
- "gpu": {
- "model": "L4",
- "count": 1,
- "vram_mb": 24576
- },
- "storage": {
- "capacity_gb": 500,
- "available_gb": 300,
- "kind": "ssd"
- },
- "network": {
- "ingress_mbps": 1000,
- "egress_mbps": 1000
- },
- "regions": [
- "us-west"
- ],
- "tags": [
- "gpu",
- "edge"
- ],
- "costs": {
- "per_hour_usd": 0.2,
- "egress_per_gb_usd": 0.01
- }
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,40 +0,0 @@
@@ -1,40 +0,0 @@
-{
- "node_id": "node-1",
- "cpu": {
- "cores": 8,
- "threads": 16
- },
- "arch": "x86_64",
- "memory_mb": 16384,
- "gpu": {
- "model": "L4",
- "count": 1,
- "vram_mb": 24576
- },
- "storage": {
- "capacity_gb": 500,
- "available_gb": 300,
- "kind": "ssd"
- },
- "network": {
- "ingress_mbps": 1000,
- "egress_mbps": 1000
- },
- "regions": [
- "us-west"
- ],
- "tags": [
- "gpu",
- "edge"
- ],
- "costs": {
- "per_hour_usd": 0.2,
- "egress_per_gb_usd": 0.01
- },
- "capabilities": [
- "node:http",
- "worker:ffmpeg"
- ],
- "agent_version": "0.9.0",
- "updated_at": "2026-10-02T00:00:00Z"
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,34 +0,0 @@
@@ -1,34 +0,0 @@
-{
- "node_id": "node-1",
- "cpu": {
- "cores": 8,
- "threads": 16
- },
- "arch": "x86_64",
- "memory_mb": 16384,
- "gpu": {
- "model": "L4",
- "count": 1,
- "vram_mb": 24576
- },
- "storage": {
- "capacity_gb": 500,
- "available_gb": 300,
- "kind": "ssd"
- },
- "network": {
- "ingress_mbps": 1000,
- "egress_mbps": 1000
- },
- "regions": [
- "us-west"
- ],
- "tags": [
- "gpu",
- "edge"
- ],
- "costs": {
- "per_hour_usd": 0.2,
- "egress_per_gb_usd": 0.01
- }
-}
@@ -10,5 +10,5 @@
@@ -10,5 +10,5 @@
"quantity": 0.35,
"quantity": 0.35,
"unit": "s",
"unit": "s",
"at": "2026-10-02T10:00:02.250Z",
"at": "2026-10-02T10:00:02.250Z",
- "source": "openvibe-node.worker"
+ "source": "openvibe-link.worker"
}
}
@@ -10,5 +10,5 @@
@@ -10,5 +10,5 @@
"quantity": 1,
"quantity": 1,
"unit": "s",
"unit": "s",
"at": "2026-10-02T10:00:00.250Z",
"at": "2026-10-02T10:00:00.250Z",
- "source": "openvibe-node.worker"
+ "source": "openvibe-link.worker"
}
}
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"share_id": "shr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
"share_id": "shr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"tunnel_token": "ovt_01J8Z3Q4R5S6T7V8W9X0Y1Z2A9_one_time",
"tunnel_token": "ovt_01J8Z3Q4R5S6T7V8W9X0Y1Z2A9_one_time",
"transport": "websocket"
"transport": "websocket"
}
}
@@ -1,4 +1,4 @@
@@ -1,4 +1,4 @@
{
{
"share_id": "shr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
"share_id": "shr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5"
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5"
}
}
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"share_id": "shr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
"share_id": "shr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"tunnel_token": "ovt_01J8Z3Q4R5S6T7V8W9X0Y1Z2A9_one_time",
"tunnel_token": "ovt_01J8Z3Q4R5S6T7V8W9X0Y1Z2A9_one_time",
"transport": "quic",
"transport": "quic",
"node_version": "0.42.0",
"node_version": "0.42.0",
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "-ab",
"name": "-ab",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"mode": "signin",
"mode": "signin",
"allowed_projects": [
"allowed_projects": [
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "agents-alice",
"name": "agents-alice",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"mode": "signin",
"mode": "signin",
"allowed_projects": [
"allowed_projects": [
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "agents-alice",
"name": "agents-alice",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"mode": "signin",
"mode": "signin",
"allowed_projects": [
"allowed_projects": [
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "agents-alice",
"name": "agents-alice",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"mode": "signin",
"mode": "signin",
"allowed_projects": [
"allowed_projects": [
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "agents-alice",
"name": "agents-alice",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"mode": "signin",
"mode": "signin",
"allowed_projects": [
"allowed_projects": [
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "agents-alice",
"name": "agents-alice",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"mode": "signin",
"mode": "signin",
"allowed_projects": [
"allowed_projects": [
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "agents-alice",
"name": "agents-alice",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"mode": "signin",
"mode": "signin",
"allowed_projects": [
"allowed_projects": [
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"mode": "signin",
"mode": "signin",
"allowed_projects": [
"allowed_projects": [
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "ab-",
"name": "ab-",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"mode": "signin",
"mode": "signin",
"allowed_projects": [
"allowed_projects": [
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "agents-alice",
"name": "agents-alice",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"mode": "signin",
"mode": "signin",
"allowed_projects": [
"allowed_projects": [
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "agents-alice",
"name": "agents-alice",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"id": "pol_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4",
"id": "pol_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4",
"mode": "signin"
"mode": "signin"
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "a-------------------------------------------------------------z",
"name": "a-------------------------------------------------------------z",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"mode": "signin",
"mode": "signin",
"allowed_projects": [
"allowed_projects": [
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "a",
"name": "a",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"mode": "signin",
"mode": "signin",
"allowed_projects": [
"allowed_projects": [
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"name": "ab",
"name": "ab",
"protocol": "http",
"protocol": "http",
"local_target": "127.0.0.1:3000",
"local_target": "127.0.0.1:3000",
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"policy": {
"policy": {
"mode": "signin",
"mode": "signin",
"allowed_projects": [
"allowed_projects": [
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
"project_id": null,
"project_id": null,
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "-ab",
"name": "-ab",
"hostname": "-ab.openvibe.website",
"hostname": "-ab.openvibe.website",
"protocol": "http",
"protocol": "http",
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
"project_id": null,
"project_id": null,
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"hostname": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.openvibe.website",
"hostname": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.openvibe.website",
"protocol": "http",
"protocol": "http",
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
"project_id": null,
"project_id": null,
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "ab-",
"name": "ab-",
"hostname": "ab-.openvibe.website",
"hostname": "ab-.openvibe.website",
"protocol": "http",
"protocol": "http",
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
"project_id": null,
"project_id": null,
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "agents-alice",
"name": "agents-alice",
"hostname": "agents-alice.openvibe.website",
"hostname": "agents-alice.openvibe.website",
"protocol": "http",
"protocol": "http",
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
"project_id": null,
"project_id": null,
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "a-------------------------------------------------------------z",
"name": "a-------------------------------------------------------------z",
"hostname": "a-------------------------------------------------------------z.openvibe.website",
"hostname": "a-------------------------------------------------------------z.openvibe.website",
"protocol": "http",
"protocol": "http",
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
"project_id": null,
"project_id": null,
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "a",
"name": "a",
"hostname": "a.openvibe.website",
"hostname": "a.openvibe.website",
"protocol": "http",
"protocol": "http",
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
"id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
},
},
"project_id": null,
"project_id": null,
- "node_id": "nod_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
+ "node_id": "lnk_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
"name": "ab",
"name": "ab",
"hostname": "ab.openvibe.website",
"hostname": "ab.openvibe.website",
"protocol": "http",
"protocol": "http",
@@ -264,14 +264,14 @@
@@ -264,14 +264,14 @@
},
},
"sub": {
"sub": {
"type": "string",
"type": "string",
- "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|node:nod_[0-9A-HJKMNP-TV-Z]{26}|agent:agt_[0-9A-HJKMNP-TV-Z]{26})$"
+ "pattern": "^(svc:[a-z][a-z0-9-]{1,39}|app:app_[0-9A-HJKMNP-TV-Z]{26}|mod:mod_[0-9A-HJKMNP-TV-Z]{26}|link:lnk_[0-9A-HJKMNP-TV-Z]{26}|agent:agt_[0-9A-HJKMNP-TV-Z]{26})$"
},
},
"actor_type": {
"actor_type": {
"enum": [
"enum": [
"service",
"service",
"app",
"app",
"mod",
"mod",
- "node",
+ "link",
"agent"
"agent"
]
]
},
},
@@ -15739,14 +15739,14 @@
@@ -15739,14 +15739,14 @@
"enum": [
"enum": [
"openai",
"openai",
"anthropic",
"anthropic",
- "node"
+ "link"
],
],
"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."
"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."
},
},
"machine": {
"machine": {
"type": "string",
"type": "string",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
+ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
},
},
"base_url": {
"base_url": {
"type": "string",
"type": "string",
@@ -15804,7 +15804,7 @@
@@ -15804,7 +15804,7 @@
"if": {
"if": {
"properties": {
"properties": {
"provider": {
"provider": {
- "const": "node"
+ "const": "link"
}
}
},
},
"required": [
"required": [
@@ -15857,7 +15857,7 @@
@@ -15857,7 +15857,7 @@
"minimax",
"minimax",
"openrouter",
"openrouter",
"custom",
"custom",
- "node"
+ "link"
],
],
"description": "anthropic = Anthropic's Messages API; openai = any OpenAI-compatible endpoint; deepseek, minimax and openrouter = those providers; custom = the endpoint in base_url (a URL that speaks the OpenAI or the Anthropic format, by the route it is used on); node = the person's own machine (no key, no base_url, no cost)."
"description": "anthropic = Anthropic's Messages API; openai = any OpenAI-compatible endpoint; deepseek, minimax and openrouter = those providers; custom = the endpoint in base_url (a URL that speaks the OpenAI or the Anthropic format, by the route it is used on); node = the person's own machine (no key, no base_url, no cost)."
},
},
@@ -15882,7 +15882,7 @@
@@ -15882,7 +15882,7 @@
"machine": {
"machine": {
"type": "string",
"type": "string",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model, or `any` of their own."
+ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model, or `any` of their own."
},
},
"models": {
"models": {
"type": "object",
"type": "object",
@@ -15929,7 +15929,7 @@
@@ -15929,7 +15929,7 @@
"if": {
"if": {
"properties": {
"properties": {
"provider": {
"provider": {
- "const": "node"
+ "const": "link"
}
}
},
},
"required": [
"required": [
@@ -16013,7 +16013,7 @@
@@ -16013,7 +16013,7 @@
"enum": [
"enum": [
"openai",
"openai",
"anthropic",
"anthropic",
- "node"
+ "link"
]
]
},
},
"machine": {
"machine": {
@@ -16022,7 +16022,7 @@
@@ -16022,7 +16022,7 @@
"null"
"null"
],
],
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
- "description": "provider node: the machine it runs on, or `any` of the person's own (contracts 0.221.0)"
+ "description": "provider link: the machine it runs on, or `any` of the person's own (contracts 0.221.0)"
},
},
"base_url": {
"base_url": {
"type": [
"type": [
@@ -16155,7 +16155,7 @@
@@ -16155,7 +16155,7 @@
"minimax",
"minimax",
"openrouter",
"openrouter",
"custom",
"custom",
- "node"
+ "link"
]
]
},
},
"machine": {
"machine": {
@@ -16164,7 +16164,7 @@
@@ -16164,7 +16164,7 @@
"null"
"null"
],
],
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
- "description": "provider node: the machine it runs on, or `any` of the person's own"
+ "description": "provider link: the machine it runs on, or `any` of the person's own"
},
},
"base_url": {
"base_url": {
"type": [
"type": [
@@ -16243,7 +16243,7 @@
@@ -16243,7 +16243,7 @@
"properties": {
"properties": {
"provider": {
"provider": {
"not": {
"not": {
- "const": "node"
+ "const": "link"
}
}
}
}
},
},
@@ -18773,7 +18773,7 @@
@@ -18773,7 +18773,7 @@
},
},
"operator": {
"operator": {
"type": "string",
"type": "string",
- "description": "Operator slug or `owner:<subject_id>` for a private node."
+ "description": "Operator slug or `owner:<subject_id>` for a private machine."
},
},
"host": {
"host": {
"type": [
"type": [
@@ -54015,7 +54015,7 @@
@@ -54015,7 +54015,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/watch/watch.v1.json",
"$id": "https://openvibe.network/contracts/watch/watch.v1.json",
"title": "Watch",
"title": "Watch",
- "description": "watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Node probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.",
+ "description": "watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Link probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
"required": [
"required": [
@@ -72957,15 +72957,15 @@
@@ -72957,15 +72957,15 @@
}
}
}
}
},
},
- "network.node": {
+ "network.link": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/network/node.v1.json",
- "title": "NetworkNode",
- "description": "network.node@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.",
+ "$id": "https://openvibe.network/contracts/network/link.v1.json",
+ "title": "NetworkLink",
+ "description": "network.link@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.",
"type": "object",
"type": "object",
"required": [
"required": [
"id",
"id",
@@ -73081,15 +73081,15 @@
@@ -73081,15 +73081,15 @@
}
}
}
}
},
},
- "network.node-report-request": {
+ "network.link-report-request": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/network/node-report-request.v1.json",
- "title": "NetworkNodeReportRequest",
- "description": "network.node-report-request@1: POST /internal/nodes/report on OpenVibe.Network (capability network.node.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.",
+ "$id": "https://openvibe.network/contracts/network/link-report-request.v1.json",
+ "title": "NetworkLinkReportRequest",
+ "description": "network.link-report-request@1: POST /internal/links/report on OpenVibe.Network (capability network.link.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.",
"type": "object",
"type": "object",
"required": [
"required": [
"source",
"source",
@@ -73106,21 +73106,21 @@
@@ -73106,21 +73106,21 @@
"type": "array",
"type": "array",
"maxItems": 500,
"maxItems": 500,
"items": {
"items": {
- "$ref": "node.v1.json"
+ "$ref": "link.v1.json"
}
}
}
}
}
}
}
}
},
},
- "network.node-list-result": {
+ "network.link-list-result": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/network/node-list-result.v1.json",
- "title": "NetworkNodeListResult",
- "description": "network.node-list-result@1: GET /api/v1/nodes on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().",
+ "$id": "https://openvibe.network/contracts/network/link-list-result.v1.json",
+ "title": "NetworkLinkListResult",
+ "description": "network.link-list-result@1: GET /api/v1/links on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().",
"type": "object",
"type": "object",
"required": [
"required": [
"nodes",
"nodes",
@@ -73131,7 +73131,7 @@
@@ -73131,7 +73131,7 @@
"nodes": {
"nodes": {
"type": "array",
"type": "array",
"items": {
"items": {
- "$ref": "node.v1.json"
+ "$ref": "link.v1.json"
}
}
},
},
"generated_at": {
"generated_at": {
@@ -73167,8 +73167,8 @@
@@ -73167,8 +73167,8 @@
},
},
"principal_id": {
"principal_id": {
"type": "string",
"type": "string",
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "The machine's node principal on Network (nod_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for."
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The machine's node principal on Network (lnk_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for."
},
},
"name": {
"name": {
"type": "string",
"type": "string",
@@ -73457,7 +73457,7 @@
@@ -73457,7 +73457,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/network/machine-pairing.v1.json",
"$id": "https://openvibe.network/contracts/network/machine-pairing.v1.json",
"title": "NetworkMachinePairing",
"title": "NetworkMachinePairing",
- "description": "network.machine-pairing@1: a one-time code that pairs a machine with the person's account (POST /api/v1/machines/pairings, capability machines.manage). The code is shown once, expires, and works once: the machine redeems it and becomes the person's. The command is what the owner runs on the machine (openvibe.bot/install). The code is never stored in the clear and never returned again.",
+ "description": "network.machine-pairing@1: a one-time code that pairs a machine with the person's account (POST /api/v1/machines/pairings, capability machines.manage). The code is shown once, expires, and works once: the machine redeems it and becomes the person's. The command is what the owner runs on the machine (openvibe.link/install). The code is never stored in the clear and never returned again.",
"type": "object",
"type": "object",
"required": [
"required": [
"code",
"code",
@@ -73484,7 +73484,7 @@
@@ -73484,7 +73484,7 @@
"installer": {
"installer": {
"type": "string",
"type": "string",
"maxLength": 500,
"maxLength": 500,
- "description": "The command the owner runs on the machine (openvibe.bot/install, with the network, the pairing id and the code)."
+ "description": "The command the owner runs on the machine (openvibe.link/install, with the network, the pairing id and the code)."
}
}
}
}
}
}
@@ -73512,7 +73512,7 @@
@@ -73512,7 +73512,7 @@
},
},
"principal_id": {
"principal_id": {
"type": "string",
"type": "string",
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
"description": "The machine's node principal on Network, when it has one."
"description": "The machine's node principal on Network, when it has one."
},
},
"owner": {
"owner": {
@@ -73550,7 +73550,7 @@
@@ -73550,7 +73550,7 @@
},
},
"principal_id": {
"principal_id": {
"type": "string",
"type": "string",
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
"description": "The machine's node principal on Network, when it has one."
"description": "The machine's node principal on Network, when it has one."
},
},
"owner": {
"owner": {
@@ -73573,7 +73573,7 @@
@@ -73573,7 +73573,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/events/payloads/network.machine.paired.v1.json",
"$id": "https://openvibe.network/contracts/events/payloads/network.machine.paired.v1.json",
"title": "NetworkMachinePairedPayload",
"title": "NetworkMachinePairedPayload",
- "description": "network.machine.paired v1 (OpenVibe.Network, plan L34: the machines hub). A machine was paired with a person's account: it redeemed the one-time code and became theirs. It holds a credential from then on; the code is spent. The machine's device id in Bot is absent until the machine checks in for the first time. Envelope: subject { type: machine, id: <nod_…> }, visibility internal, actor service:network.",
+ "description": "network.machine.paired v1 (OpenVibe.Network, plan L34: the machines hub). A machine was paired with a person's account: it redeemed the one-time code and became theirs. It holds a credential from then on; the code is spent. The machine's device id in Bot is absent until the machine checks in for the first time. Envelope: subject { type: machine, id: <lnk_…> }, visibility internal, actor service:network.",
"type": "object",
"type": "object",
"required": [
"required": [
"principal_id",
"principal_id",
@@ -73594,7 +73594,7 @@
@@ -73594,7 +73594,7 @@
},
},
"principal_id": {
"principal_id": {
"type": "string",
"type": "string",
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
"description": "The node principal the pairing created. Also the envelope subject id."
"description": "The node principal the pairing created. Also the envelope subject id."
},
},
"owner": {
"owner": {
@@ -73680,7 +73680,7 @@
@@ -73680,7 +73680,7 @@
},
},
"principal_id": {
"principal_id": {
"type": "string",
"type": "string",
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
"description": "The machine's node principal on Network, when it had one."
"description": "The machine's node principal on Network, when it had one."
},
},
"owner": {
"owner": {
@@ -73879,7 +73879,7 @@
@@ -73879,7 +73879,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/platform/resource-offer.v1.json",
"$id": "https://openvibe.network/contracts/platform/resource-offer.v1.json",
"title": "ResourceOffer",
"title": "ResourceOffer",
- "description": "What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.node@1) deliberately leaves out.",
+ "description": "What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.link@1) deliberately leaves out.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
"required": [
"required": [
@@ -73910,7 +73910,7 @@
@@ -73910,7 +73910,7 @@
},
},
"node_id": {
"node_id": {
"type": "string",
"type": "string",
- "description": "network.node@1 id, for kind node"
+ "description": "network.link@1 id, for kind node"
},
},
"provider": {
"provider": {
"type": "string",
"type": "string",
@@ -73948,7 +73948,7 @@
@@ -73948,7 +73948,7 @@
"type": "string",
"type": "string",
"pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"
"pattern": "^[a-z][a-z0-9-]*:[a-z0-9.-]+$"
},
},
- "description": "e.g. node:http, worker:browser, worker:ffmpeg, worker:ai-gpu, events:gateway, events:durable, object:r2. 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 Link'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. Beside them, a kind harness names `runtime:<class>` for each platform.runtime-class@1 class it can run (runtime:function, runtime:code, runtime:browser, runtime:linux, runtime:desktop, runtime:gpu); these are ordinary capability names, not reserved, and OpenVibe.Codes already advertises the classes it offers."
+ "description": "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 Link's job worker, one per class: worker:function, worker:code, worker:browser, worker:linux, worker:desktop and worker:gpu ($defs.reservedWorkerCapabilities). A Link advertises the name of each class it runs (Link's status.capabilities.worker); every other `worker:` name (worker:ffmpeg, worker:ai-gpu) is an ordinary capability name. Beside them, a kind harness names `runtime:<class>` for each platform.runtime-class@1 class it can run (runtime:function, runtime:code, runtime:browser, runtime:linux, runtime:desktop, runtime:gpu); these are ordinary capability names, not reserved, and OpenVibe.Codes already advertises the classes it offers."
},
},
"capacity": {
"capacity": {
"type": "object",
"type": "object",
@@ -75221,7 +75221,7 @@
@@ -75221,7 +75221,7 @@
"quantity": 1,
"quantity": 1,
"unit": "s",
"unit": "s",
"at": "2026-10-02T10:00:00.250Z",
"at": "2026-10-02T10:00:00.250Z",
- "source": "openvibe-node.worker"
+ "source": "openvibe-link.worker"
}
}
]
]
}
}
@@ -75357,15 +75357,222 @@
@@ -75357,15 +75357,222 @@
]
]
}
}
},
},
- "platform.node-capabilities": {
+ "link.compute": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/platform/node-capabilities.v1.json",
- "title": "NodeCapabilities",
- "description": "Node hardware, placement regions, tags, and operating costs (T1 Universal Fabric; ADR-034 proposed).",
+ "$id": "https://openvibe.network/contracts/link/compute.v1.json",
+ "title": "LinkCompute",
+ "description": "Where a person's work can run. Only ready candidates may be picked; a missing rental quote is never a price.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "sources",
+ "candidates"
+ ],
+ "properties": {
+ "sources": {
+ "type": "array",
+ "items": {
+ "$ref": "#/$defs/source"
+ }
+ },
+ "candidates": {
+ "type": "array",
+ "items": {
+ "$ref": "#/$defs/candidate"
+ }
+ }
+ },
+ "$defs": {
+ "kind": {
+ "enum": [
+ "personal",
+ "cloud",
+ "exchange",
+ "community"
+ ]
+ },
+ "status": {
+ "enum": [
+ "ready",
+ "unavailable",
+ "coming_soon"
+ ]
+ },
+ "price": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": false,
+ "required": [
+ "amount_usd",
+ "unit"
+ ],
+ "properties": {
+ "amount_usd": {
+ "type": "number",
+ "minimum": 0
+ },
+ "unit": {
+ "enum": [
+ "hour"
+ ]
+ }
+ }
+ },
+ "source": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "kind",
+ "name",
+ "status",
+ "reason"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1
+ },
+ "principal_id": {
+ "type": "string",
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "kind": {
+ "$ref": "#/$defs/kind"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "status": {
+ "$ref": "#/$defs/status"
+ },
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "price": {
+ "$ref": "#/$defs/price"
+ },
+ "quoted_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "expires_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "if": {
+ "properties": {
+ "kind": {
+ "const": "exchange"
+ },
+ "status": {
+ "const": "ready"
+ }
+ },
+ "required": [
+ "kind",
+ "status"
+ ]
+ },
+ "then": {
+ "required": [
+ "price",
+ "quoted_at",
+ "expires_at"
+ ],
+ "properties": {
+ "price": {
+ "type": "object"
+ }
+ }
+ }
+ },
+ "candidate": {
+ "allOf": [
+ {
+ "$ref": "#/$defs/source"
+ },
+ {
+ "type": "object",
+ "required": [
+ "id",
+ "price"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ }
+ ]
+ }
+ },
+ "examples": [
+ {
+ "sources": [
+ {
+ "kind": "personal",
+ "name": "Your Link machines",
+ "status": "ready",
+ "reason": null
+ },
+ {
+ "kind": "cloud",
+ "name": "OpenVibe cloud machines",
+ "status": "coming_soon",
+ "reason": "No cloud pool is accepting jobs yet."
+ },
+ {
+ "kind": "exchange",
+ "name": "Exchange GPU rentals",
+ "status": "coming_soon",
+ "reason": "A live quote is required.",
+ "price": null
+ },
+ {
+ "kind": "community",
+ "name": "Community machines",
+ "status": "coming_soon",
+ "reason": "Shared placement is being connected."
+ }
+ ],
+ "candidates": [
+ {
+ "id": "dev_01J0000000000000000000000",
+ "kind": "personal",
+ "name": "Studio",
+ "status": "ready",
+ "reason": null,
+ "price": {
+ "amount_usd": 0,
+ "unit": "hour"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "platform.link-capabilities": {
+ "version": "1.0.0",
+ "owner": "network",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/platform/link-capabilities.v1.json",
+ "title": "LinkCapabilities",
+ "description": "Link machine hardware, placement regions, tags, and operating costs (T1 Universal Fabric; ADR-034 proposed).",
"$comment": "Additive optional fields may extend this v1 contract.",
"$comment": "Additive optional fields may extend this v1 contract.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
@@ -76923,7 +77130,7 @@
@@ -76923,7 +77130,7 @@
{
{
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
- "description": "Device to server: wall-clock second `second` of the job has fully elapsed. Metering (platform.usage-sample@1, written through billing.usage.record by whoever terminates the link: OpenVibe.Bot now, Fabric later): second n is [started_ms + n*1000, started_ms + (n+1)*1000) on the worker's monotonic clock. A worker sends `job_usage` for n only once that second has fully elapsed, so every `job_usage` stands for quantity 1; the partial last second is never sent here, only in `job_exit`. Each second is exactly one reading: `id` and `idempotency_key` `run:<job id>:<n>`, `service` `run`, `operation` `function.invoke`, `unit` `s`, `quantity` 1 (the partial last second: (wall_ms mod 1000)/1000, never 0), `at` new Date(started_ms + n*1000).toISOString(), `resource` the job id, `node` the worker's device id, `source` `openvibe-node.worker`, and `project`, `subject` and `provider` from the dispatcher's own record of the job. No field depends on when or how often a frame was sent, and no rating fields (`cost_estimate`, `free_allowance_used`, `vibes_charged`) are set, because Billing keeps the first reading under a key, replays an identical one and refuses a different one (409): a resend after a reconnect, a `job_usage` and the `job_exit` backfill of the same second, and two writers of one job all land on one record.",
+ "description": "Device to server: wall-clock second `second` of the job has fully elapsed. Metering (platform.usage-sample@1, written through billing.usage.record by whoever terminates the link: OpenVibe.Bot now, Fabric later): second n is [started_ms + n*1000, started_ms + (n+1)*1000) on the worker's monotonic clock. A worker sends `job_usage` for n only once that second has fully elapsed, so every `job_usage` stands for quantity 1; the partial last second is never sent here, only in `job_exit`. Each second is exactly one reading: `id` and `idempotency_key` `run:<job id>:<n>`, `service` `run`, `operation` `function.invoke`, `unit` `s`, `quantity` 1 (the partial last second: (wall_ms mod 1000)/1000, never 0), `at` new Date(started_ms + n*1000).toISOString(), `resource` the job id, `node` the worker's device id, `source` `openvibe-link.worker`, and `project`, `subject` and `provider` from the dispatcher's own record of the job. No field depends on when or how often a frame was sent, and no rating fields (`cost_estimate`, `free_allowance_used`, `vibes_charged`) are set, because Billing keeps the first reading under a key, replays an identical one and refuses a different one (409): a resend after a reconnect, a `job_usage` and the `job_exit` backfill of the same second, and two writers of one job all land on one record.",
"required": [
"required": [
"v",
"v",
"seq",
"seq",
@@ -82245,7 +82452,7 @@
@@ -82245,7 +82452,7 @@
"null"
"null"
],
],
"maxLength": 128,
"maxLength": 128,
- "description": "The offer's provider, as Run sent it; null for a first-party node."
+ "description": "The offer's provider, as Run sent it; null for a first-party machine."
},
},
"cancel_requested": {
"cancel_requested": {
"type": "boolean",
"type": "boolean",
@@ -83789,7 +83996,7 @@
@@ -83789,7 +83996,7 @@
"null"
"null"
],
],
"maxLength": 128,
"maxLength": 128,
- "description": "The offer's provider (the usage reading's `provider`); null for a first-party node."
+ "description": "The offer's provider (the usage reading's `provider`); null for a first-party machine."
},
},
"region": {
"region": {
"type": [
"type": [
@@ -85328,15 +85535,15 @@
@@ -85328,15 +85535,15 @@
}
}
}
}
},
},
- "network.node.revoked": {
+ "network.link.revoked": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "network",
"owner": "network",
"status": "active",
"status": "active",
"schema": {
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://openvibe.network/contracts/events/payloads/network.node.revoked.v1.json",
- "title": "NetworkNodeRevokedPayload",
- "description": "network.node.revoked v1 (OpenVibe.Network; plan T2, docs/t2-cells-and-node-principal.md section 10, migrations/0014_node_pairing.sql). A node principal (nod_…), a person's paired machine, was revoked: through POST /internal/node-principals/:id/revoke (network.node.manage, the service that paired it), through POST /api/v1/me/nodes/:id/revoke (the owner's session), or with the owner's account. Network revokes the row and clears the credential and its grace window; a machine holding a live node token is refused at its next use (≤ 300 s), and until this event only learned of the revoke at its next reauth (≤ 330 s). A consumer stops the machine at once; a second revoke changes nothing and emits nothing. The credential and its hash are never carried. Envelope: subject { type: node_principal, id: <nod_…> }, visibility internal, actor the service (svc:<service>) or the owner ({ type: user }) that revoked.",
+ "$id": "https://openvibe.network/contracts/events/payloads/network.link.revoked.v1.json",
+ "title": "NetworkLinkRevokedPayload",
+ "description": "network.link.revoked v1 (OpenVibe.Network; plan T2, docs/t2-cells-and-node-principal.md section 10, migrations/0014_node_pairing.sql). A node principal (lnk_…), a person's paired machine, was revoked: through POST /internal/link-principals/:id/revoke (network.link.manage, the service that paired it), through POST /api/v1/me/nodes/:id/revoke (the owner's session), or with the owner's account. Network revokes the row and clears the credential and its grace window; a machine holding a live node token is refused at its next use (≤ 300 s), and until this event only learned of the revoke at its next reauth (≤ 330 s). A consumer stops the machine at once; a second revoke changes nothing and emits nothing. The credential and its hash are never carried. Envelope: subject { type: node_principal, id: <lnk_…> }, visibility internal, actor the service (svc:<service>) or the owner ({ type: user }) that revoked.",
"type": "object",
"type": "object",
"required": [
"required": [
"node_id",
"node_id",
@@ -85353,7 +85560,7 @@
@@ -85353,7 +85560,7 @@
},
},
"principal_id": {
"principal_id": {
"type": "string",
"type": "string",
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
"description": "The revoked node principal. Also the envelope subject id."
"description": "The revoked node principal. Also the envelope subject id."
},
},
"owner": {
"owner": {
@@ -103862,7 +104069,7 @@
@@ -103862,7 +104069,7 @@
"node",
"node",
"submitted"
"submitted"
],
],
- "description": "codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it."
+ "description": "codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it."
},
},
"id": {
"id": {
"type": "string",
"type": "string",
@@ -104111,7 +104318,7 @@
@@ -104111,7 +104318,7 @@
"node",
"node",
"submitted"
"submitted"
],
],
- "description": "codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it."
+ "description": "codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it."
},
},
"id": {
"id": {
"type": "string",
"type": "string",
@@ -105252,7 +105459,7 @@
@@ -105252,7 +105459,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/website/share.v1.json",
"$id": "https://openvibe.network/contracts/website/share.v1.json",
"title": "WebsiteShare",
"title": "WebsiteShare",
- "description": "website.share@1: one Share on OpenVibe.Website Connect, a local port published at a stable hostname through the edge relay. The record says who owns it, which project scopes it, which Node tunnel serves it, which local target it may reach, which policy version the edge enforces and where it is in its life (draft, review, active, paused, revoked). The tunnel token, the request bodies the inspector captures and the policy's password hash are never part of this document.",
+ "description": "website.share@1: one Share on OpenVibe.Website Connect, a local port published at a stable hostname through the edge relay. The record says who owns it, which project scopes it, which Link tunnel serves it, which local target it may reach, which policy version the edge enforces and where it is in its life (draft, review, active, paused, revoked). The tunnel token, the request bodies the inspector captures and the policy's password hash are never part of this document.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
"required": [
"required": [
@@ -105294,7 +105501,7 @@
@@ -105294,7 +105501,7 @@
"string",
"string",
"null"
"null"
],
],
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
"description": "The Network node principal expected to serve the tunnel; null until one is bound."
"description": "The Network node principal expected to serve the tunnel; null until one is bound."
},
},
"name": {
"name": {
@@ -105372,7 +105579,7 @@
@@ -105372,7 +105579,7 @@
"null"
"null"
],
],
"format": "date-time",
"format": "date-time",
- "description": "The last successful Node liveness check; null until the first tunnel."
+ "description": "The last successful Link liveness check; null until the first tunnel."
},
},
"created_at": {
"created_at": {
"type": "string",
"type": "string",
@@ -105455,8 +105662,8 @@
@@ -105455,8 +105662,8 @@
},
},
"node_id": {
"node_id": {
"type": "string",
"type": "string",
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "Optional: bind the share to this Node principal now. Omit to bind it when the first tunnel connects."
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "Optional: bind the share to this Link principal now. Omit to bind it when the first tunnel connects."
},
},
"region": {
"region": {
"type": "string",
"type": "string",
@@ -105489,7 +105696,7 @@
@@ -105489,7 +105696,7 @@
},
},
"node_id": {
"node_id": {
"type": "string",
"type": "string",
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
"description": "Must equal the node principal in the Network token; a mismatch is refused and audited."
"description": "Must equal the node principal in the Network token; a mismatch is refused and audited."
},
},
"tunnel_token": {
"tunnel_token": {
@@ -105995,7 +106202,7 @@
@@ -105995,7 +106202,7 @@
"string",
"string",
"null"
"null"
],
],
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
"description": "The Network node principal the device presents; null for an agent or server that joins through a pre-auth key."
"description": "The Network node principal the device presents; null for an agent or server that joins through a pre-auth key."
},
},
"name": {
"name": {
@@ -111902,7 +112109,7 @@
@@ -111902,7 +112109,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/bot/machine-pool.v1.json",
"$id": "https://openvibe.network/contracts/bot/machine-pool.v1.json",
"title": "BotMachinePool",
"title": "BotMachinePool",
- "description": "bot.machine-pool@1 (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC, `rpc-server` on the members and `llama-server --rpc` on the head. OpenVibe.Network holds the record: the owner proposes it on /machines (POST /api/v1/machine-pools, two to eight of their own machines and an optional head; the default head has the most free VRAM), and each machine reads it with its own node token (GET /api/v1/node/self/pools/:id, network.node.self.manage) and approves it locally (POST /api/v1/node/self/pools/:id/approve with { certificate }). A machine's `certificate` is null until it approves on that machine: the Link generates an Ed25519 key there, keeps the private key, and uploads only its self-signed public certificate, which the other members pin for the pool's private link. A pool with any null certificate never serves. The record never carries a private key or a prompt. What a schema cannot say, Network and Node check at run time: the members are distinct and the head is one of them; every member has the pool's owner, today, through Bot; every member was seen from the same public address with a private address in the same subnet; the head's direct link probe to each member passes; each machine's certificate is its own; and any change of membership, owner, head or key revokes the pool, so all machines approve a new one. A machine that leaves (removed, signed out, a different owner) drops its link and its key at once.",
+ "description": "bot.machine-pool@1 (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC, `rpc-server` on the members and `llama-server --rpc` on the head. OpenVibe.Network holds the record: the owner proposes it on /machines (POST /api/v1/machine-pools, two to eight of their own machines and an optional head; the default head has the most free VRAM), and each machine reads it with its own Link token (GET /api/v1/link/self/pools/:id, network.link.self.manage) and approves it locally (POST /api/v1/link/self/pools/:id/approve with { certificate }). A machine's `certificate` is null until it approves on that machine: the Link generates an Ed25519 key there, keeps the private key, and uploads only its self-signed public certificate, which the other members pin for the pool's private link. A pool with any null certificate never serves. The record never carries a private key or a prompt. What a schema cannot say, Network and Link check at run time: the members are distinct and the head is one of them; every member has the pool's owner, today, through Bot; every member was seen from the same public address with a private address in the same subnet; the head's direct link probe to each member passes; each machine's certificate is its own; and any change of membership, owner, head or key revokes the pool, so all machines approve a new one. A machine that leaves (removed, signed out, a different owner) drops its link and its key at once.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
"required": [
"required": [
@@ -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.Actor API",
"title": "OpenVibe.Actor API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.AI API",
"title": "OpenVibe.AI API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"
@@ -773,7 +773,7 @@
@@ -773,7 +773,7 @@
"enum": [
"enum": [
"openai",
"openai",
"anthropic",
"anthropic",
- "node"
+ "link"
],
],
"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."
"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."
},
},
@@ -786,9 +786,9 @@
@@ -786,9 +786,9 @@
"schema": {
"schema": {
"type": "string",
"type": "string",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
+ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
},
},
- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
+ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
},
},
{
{
"name": "base_url",
"name": "base_url",
@@ -928,7 +928,7 @@
@@ -928,7 +928,7 @@
"enum": [
"enum": [
"openai",
"openai",
"anthropic",
"anthropic",
- "node"
+ "link"
],
],
"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."
"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."
},
},
@@ -941,9 +941,9 @@
@@ -941,9 +941,9 @@
"schema": {
"schema": {
"type": "string",
"type": "string",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
+ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
},
},
- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
+ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
},
},
{
{
"name": "base_url",
"name": "base_url",
@@ -5296,14 +5296,14 @@
@@ -5296,14 +5296,14 @@
"enum": [
"enum": [
"openai",
"openai",
"anthropic",
"anthropic",
- "node"
+ "link"
],
],
"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."
"description": "openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost)."
},
},
"machine": {
"machine": {
"type": "string",
"type": "string",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
- "description": "provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
+ "description": "provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine."
},
},
"base_url": {
"base_url": {
"type": "string",
"type": "string",
@@ -5361,7 +5361,7 @@
@@ -5361,7 +5361,7 @@
"if": {
"if": {
"properties": {
"properties": {
"provider": {
"provider": {
- "const": "node"
+ "const": "link"
}
}
},
},
"required": [
"required": [
@@ -5416,7 +5416,7 @@
@@ -5416,7 +5416,7 @@
"enum": [
"enum": [
"openai",
"openai",
"anthropic",
"anthropic",
- "node"
+ "link"
]
]
},
},
"machine": {
"machine": {
@@ -5425,7 +5425,7 @@
@@ -5425,7 +5425,7 @@
"null"
"null"
],
],
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
"pattern": "^(dev_[0-9A-HJKMNP-TV-Z]{26}|any)$",
- "description": "provider node: the machine it runs on, or `any` of the person's own (contracts 0.221.0)"
+ "description": "provider link: the machine it runs on, or `any` of the person's own (contracts 0.221.0)"
},
},
"base_url": {
"base_url": {
"type": [
"type": [
@@ -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.Bench API",
"title": "OpenVibe.Bench API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"
@@ -2462,7 +2462,7 @@
@@ -2462,7 +2462,7 @@
"node",
"node",
"submitted"
"submitted"
],
],
- "description": "codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it."
+ "description": "codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it."
},
},
"id": {
"id": {
"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.Billing API",
"title": "OpenVibe.Billing API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"
@@ -6456,7 +6456,7 @@
@@ -6456,7 +6456,7 @@
"quantity": 1,
"quantity": 1,
"unit": "s",
"unit": "s",
"at": "2026-10-02T10:00:00.250Z",
"at": "2026-10-02T10:00:00.250Z",
- "source": "openvibe-node.worker"
+ "source": "openvibe-link.worker"
}
}
]
]
}
}
@@ -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.371.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"
@@ -476,7 +476,7 @@
@@ -476,7 +476,7 @@
"get": {
"get": {
"operationId": "get_api_v1_machines",
"operationId": "get_api_v1_machines",
"summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",
"summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",
- "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.network/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",
+ "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.link/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",
"tags": [
"tags": [
"bot.machine"
"bot.machine"
],
],
@@ -525,7 +525,7 @@
@@ -525,7 +525,7 @@
"get": {
"get": {
"operationId": "get_api_v1_machines_id",
"operationId": "get_api_v1_machines_id",
"summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",
"summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",
- "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.network/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",
+ "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.link/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",
"tags": [
"tags": [
"bot.machine"
"bot.machine"
],
],
@@ -582,7 +582,7 @@
@@ -582,7 +582,7 @@
"patch": {
"patch": {
"operationId": "patch_api_v1_machines_id",
"operationId": "patch_api_v1_machines_id",
"summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",
"summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",
- "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.network/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",
+ "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.link/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",
"tags": [
"tags": [
"bot.machine"
"bot.machine"
],
],
@@ -992,7 +992,7 @@
@@ -992,7 +992,7 @@
"post": {
"post": {
"operationId": "post_api_v1_machines_id_revoke",
"operationId": "post_api_v1_machines_id_revoke",
"summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",
"summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",
- "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.network/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",
+ "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.link/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",
"tags": [
"tags": [
"bot.machine"
"bot.machine"
],
],
@@ -1058,7 +1058,7 @@
@@ -1058,7 +1058,7 @@
"put": {
"put": {
"operationId": "put_api_v1_machines_id_roles",
"operationId": "put_api_v1_machines_id_roles",
"summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",
"summary": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject).",
- "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.network/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",
+ "description": "**bot.machine.manage** (first-party): A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.link/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",
"tags": [
"tags": [
"bot.machine"
"bot.machine"
],
],
@@ -2563,7 +2563,7 @@
@@ -2563,7 +2563,7 @@
"null"
"null"
],
],
"maxLength": 128,
"maxLength": 128,
- "description": "The offer's provider, as Run sent it; null for a first-party node."
+ "description": "The offer's provider, as Run sent it; null for a first-party machine."
},
},
"cancel_requested": {
"cancel_requested": {
"type": "boolean",
"type": "boolean",
@@ -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.371.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.372.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 Connect API",
"title": "OpenVibe Connect API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Design API",
"title": "OpenVibe.Design API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Domains API",
"title": "OpenVibe.Domains API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Exchange API",
"title": "OpenVibe.Exchange API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Exchange as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Exchange as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Food API",
"title": "OpenVibe.Food API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Inventory API",
"title": "OpenVibe.Inventory API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Mail API",
"title": "OpenVibe.Mail API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.MediaHub API",
"title": "OpenVibe.MediaHub API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"
@@ -192,38 +192,70 @@
@@ -192,38 +192,70 @@
}
}
}
}
},
},
- "/api/v1/machines": {
- "get": {
- "operationId": "get_api_v1_machines",
- "summary": "Read a person's own machines: the list, one machine, and the catalogue of roles.",
- "description": "**network.machines.read** (public): Read a person's own machines: the list, one machine, and the catalogue of roles. A person reads their own with their own token; an app needs the person's consent for this scope (it is a public capability, so it can be granted to an app, and the app then acts only on behalf of the person who consented).",
+ "/api/v1/link/self/capabilities": {
+ "put": {
+ "operationId": "put_api_v1_link_self_capabilities",
+ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
+ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
"tags": [
"tags": [
- "network.machines"
+ "network.link"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.machines.read"
+ "network.link.self.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "public"
+ "internal"
],
],
- "x-openvibe-quota-class": "network-read",
+ "x-openvibe-quota-class": "admin",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.machines.read"
+ "network.link.self.manage"
]
]
}
}
],
],
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success",
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
"content": {
"content": {
- "application/json": {
+ "application/problem+json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/network.machine-list-result.v1"
+ "$ref": "#/components/schemas/errors.problem.v1"
}
}
}
}
}
}
+ }
+ }
+ }
+ },
+ "/api/v1/link/self/credential": {
+ "post": {
+ "operationId": "post_api_v1_link_self_credential",
+ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
+ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
+ "tags": [
+ "network.link"
+ ],
+ "x-openvibe-capabilities": [
+ "network.link.self.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "admin",
+ "security": [
+ {
+ "openvibeToken": [
+ "network.link.self.manage"
+ ]
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -238,25 +270,25 @@
@@ -238,25 +270,25 @@
}
}
}
}
},
},
- "/api/v1/machines/{id}": {
+ "/api/v1/link/self/pools/{id}": {
"get": {
"get": {
- "operationId": "get_api_v1_machines_id",
- "summary": "Read a person's own machines: the list, one machine, and the catalogue of roles.",
- "description": "**network.machines.read** (public): Read a person's own machines: the list, one machine, and the catalogue of roles. A person reads their own with their own token; an app needs the person's consent for this scope (it is a public capability, so it can be granted to an app, and the app then acts only on behalf of the person who consented).",
+ "operationId": "get_api_v1_link_self_pools_id",
+ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
+ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
"tags": [
"tags": [
- "network.machines"
+ "network.link"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.machines.read"
+ "network.link.self.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "public"
+ "internal"
],
],
- "x-openvibe-quota-class": "network-read",
+ "x-openvibe-quota-class": "admin",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.machines.read"
+ "network.link.self.manage"
]
]
}
}
],
],
@@ -272,14 +304,7 @@
@@ -272,14 +304,7 @@
],
],
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/network.machine-list-result.v1"
- }
- }
- }
+ "description": "Success"
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -292,38 +317,30 @@
@@ -292,38 +317,30 @@
}
}
}
}
}
}
- },
- "patch": {
- "operationId": "patch_api_v1_machines_id",
- "summary": "Pair, rename, give roles to and revoke a person's own machines.",
- "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.",
+ }
+ },
+ "/api/v1/link/self/pools/{id}/approve": {
+ "post": {
+ "operationId": "post_api_v1_link_self_pools_id_approve",
+ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
+ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
"tags": [
"tags": [
- "network.machines"
+ "network.link"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.machines.manage"
+ "network.link.self.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "public"
+ "internal"
],
],
- "x-openvibe-quota-class": "network-write",
+ "x-openvibe-quota-class": "admin",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.machines.manage"
+ "network.link.self.manage"
]
]
}
}
],
],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/bot.machine-manage-request.v1"
- }
- }
- }
- },
"parameters": [
"parameters": [
{
{
"name": "id",
"name": "id",
@@ -336,14 +353,7 @@
@@ -336,14 +353,7 @@
],
],
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/network.machine-manage-result.v1"
- }
- }
- }
+ "description": "Success"
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -358,38 +368,28 @@
@@ -358,38 +368,28 @@
}
}
}
}
},
},
- "/api/v1/machines/{id}/revoke": {
+ "/api/v1/link/self/pools/{id}/leave": {
"post": {
"post": {
- "operationId": "post_api_v1_machines_id_revoke",
- "summary": "Pair, rename, give roles to and revoke a person's own machines.",
- "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.",
+ "operationId": "post_api_v1_link_self_pools_id_leave",
+ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
+ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
"tags": [
"tags": [
- "network.machines"
+ "network.link"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.machines.manage"
+ "network.link.self.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "public"
+ "internal"
],
],
- "x-openvibe-quota-class": "network-write",
+ "x-openvibe-quota-class": "admin",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.machines.manage"
+ "network.link.self.manage"
]
]
}
}
],
],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/bot.machine-manage-request.v1"
- }
- }
- }
- },
"parameters": [
"parameters": [
{
{
"name": "id",
"name": "id",
@@ -402,14 +402,7 @@
@@ -402,14 +402,7 @@
],
],
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/network.machine-manage-result.v1"
- }
- }
- }
+ "description": "Success"
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -424,38 +417,28 @@
@@ -424,38 +417,28 @@
}
}
}
}
},
},
- "/api/v1/machines/{id}/roles": {
- "put": {
- "operationId": "put_api_v1_machines_id_roles",
- "summary": "Pair, rename, give roles to and revoke a person's own machines.",
- "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.",
+ "/api/v1/link/self/pools/{id}/status": {
+ "post": {
+ "operationId": "post_api_v1_link_self_pools_id_status",
+ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
+ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
"tags": [
"tags": [
- "network.machines"
+ "network.link"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.machines.manage"
+ "network.link.self.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "public"
+ "internal"
],
],
- "x-openvibe-quota-class": "network-write",
+ "x-openvibe-quota-class": "admin",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.machines.manage"
+ "network.link.self.manage"
]
]
}
}
],
],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/bot.machine-manage-request.v1"
- }
- }
- }
- },
"parameters": [
"parameters": [
{
{
"name": "id",
"name": "id",
@@ -468,14 +451,7 @@
@@ -468,14 +451,7 @@
],
],
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/network.machine-manage-result.v1"
- }
- }
- }
+ "description": "Success"
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -490,48 +466,31 @@
@@ -490,48 +466,31 @@
}
}
}
}
},
},
- "/api/v1/machines/pairings": {
+ "/api/v1/link/self/pools/announce": {
"post": {
"post": {
- "operationId": "post_api_v1_machines_pairings",
- "summary": "Pair, rename, give roles to and revoke a person's own machines.",
- "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.",
+ "operationId": "post_api_v1_link_self_pools_announce",
+ "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
+ "description": "**network.link.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
"tags": [
"tags": [
- "network.machines"
+ "network.link"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.machines.manage"
+ "network.link.self.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "public"
+ "internal"
],
],
- "x-openvibe-quota-class": "network-write",
+ "x-openvibe-quota-class": "admin",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.machines.manage"
+ "network.link.self.manage"
]
]
}
}
],
],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/bot.machine-manage-request.v1"
- }
- }
- }
- },
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/network.machine-manage-result.v1"
- }
- }
- }
+ "description": "Success"
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -546,9 +505,9 @@
@@ -546,9 +505,9 @@
}
}
}
}
},
},
- "/api/v1/machines/roles": {
+ "/api/v1/machines": {
"get": {
"get": {
- "operationId": "get_api_v1_machines_roles",
+ "operationId": "get_api_v1_machines",
"summary": "Read a person's own machines: the list, one machine, and the catalogue of roles.",
"summary": "Read a person's own machines: the list, one machine, and the catalogue of roles.",
"description": "**network.machines.read** (public): Read a person's own machines: the list, one machine, and the catalogue of roles. A person reads their own with their own token; an app needs the person's consent for this scope (it is a public capability, so it can be granted to an app, and the app then acts only on behalf of the person who consented).",
"description": "**network.machines.read** (public): Read a person's own machines: the list, one machine, and the catalogue of roles. A person reads their own with their own token; an app needs the person's consent for this scope (it is a public capability, so it can be granted to an app, and the app then acts only on behalf of the person who consented).",
"tags": [
"tags": [
@@ -592,35 +551,45 @@
@@ -592,35 +551,45 @@
}
}
}
}
},
},
- "/api/v1/me/actions": {
+ "/api/v1/machines/{id}": {
"get": {
"get": {
- "operationId": "get_api_v1_me_actions",
- "summary": "The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and f",
- "description": "**network.actions.read** (public): The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and failed (network.me-actions-result@1); POST /api/v1/me/actions/:service/:id/:action approves, declines, cancels or retries through the owning authority. Readable from every OpenVibe site with the person's Network token, like the bell.",
+ "operationId": "get_api_v1_machines_id",
+ "summary": "Read a person's own machines: the list, one machine, and the catalogue of roles.",
+ "description": "**network.machines.read** (public): Read a person's own machines: the list, one machine, and the catalogue of roles. A person reads their own with their own token; an app needs the person's consent for this scope (it is a public capability, so it can be granted to an app, and the app then acts only on behalf of the person who consented).",
"tags": [
"tags": [
- "network.actions"
+ "network.machines"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.actions.read"
+ "network.machines.read"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
"public"
"public"
],
],
- "x-openvibe-quota-class": "standard",
+ "x-openvibe-quota-class": "network-read",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.actions.read"
+ "network.machines.read"
]
]
}
}
],
],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
"responses": {
"responses": {
"2XX": {
"2XX": {
"description": "Success",
"description": "Success",
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/network.me-actions-result.v1"
+ "$ref": "#/components/schemas/network.machine-list-result.v1"
}
}
}
}
}
}
@@ -636,39 +605,39 @@
@@ -636,39 +605,39 @@
}
}
}
}
}
}
- }
- },
- "/api/v1/me/actions/{service}/{id}/{action}": {
- "post": {
- "operationId": "post_api_v1_me_actions_service_id_action",
- "summary": "The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and f",
- "description": "**network.actions.read** (public): The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and failed (network.me-actions-result@1); POST /api/v1/me/actions/:service/:id/:action approves, declines, cancels or retries through the owning authority. Readable from every OpenVibe site with the person's Network token, like the bell.",
+ },
+ "patch": {
+ "operationId": "patch_api_v1_machines_id",
+ "summary": "Pair, rename, give roles to and revoke a person's own machines.",
+ "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.",
"tags": [
"tags": [
- "network.actions"
+ "network.machines"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.actions.read"
+ "network.machines.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
"public"
"public"
],
],
- "x-openvibe-quota-class": "standard",
+ "x-openvibe-quota-class": "network-write",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.actions.read"
+ "network.machines.manage"
]
]
}
}
],
],
- "parameters": [
- {
- "name": "service",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/bot.machine-manage-request.v1"
+ }
}
}
- },
+ }
+ },
+ "parameters": [
{
{
"name": "id",
"name": "id",
"in": "path",
"in": "path",
@@ -676,14 +645,6 @@
@@ -676,14 +645,6 @@
"schema": {
"schema": {
"type": "string"
"type": "string"
}
}
- },
- {
- "name": "action",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
}
}
],
],
"responses": {
"responses": {
@@ -692,7 +653,7 @@
@@ -692,7 +653,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/network.me-actions-result.v1"
+ "$ref": "#/components/schemas/network.machine-manage-result.v1"
}
}
}
}
}
}
@@ -710,25 +671,25 @@
@@ -710,25 +671,25 @@
}
}
}
}
},
},
- "/api/v1/me/actions/skills/{id}/run": {
+ "/api/v1/machines/{id}/revoke": {
"post": {
"post": {
- "operationId": "post_api_v1_me_actions_skills_id_run",
- "summary": "Start one of Actor's catalogue skills as the signed-in person (plan 2026-11-09 §7): POST /api/v1/me/actions/skills/:id/run asks Actor to run that skill for them, with the input and the choice.",
- "description": "**network.skill.run** (public): Start one of Actor's catalogue skills as the signed-in person (plan 2026-11-09 §7): POST /api/v1/me/actions/skills/:id/run asks Actor to run that skill for them, with the input and the choice. Network never runs the skill itself; Actor does, under the person's own approvals and budget.",
+ "operationId": "post_api_v1_machines_id_revoke",
+ "summary": "Pair, rename, give roles to and revoke a person's own machines.",
+ "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.",
"tags": [
"tags": [
- "network.skill"
+ "network.machines"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.skill.run"
+ "network.machines.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
"public"
"public"
],
],
- "x-openvibe-quota-class": "standard",
+ "x-openvibe-quota-class": "network-write",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.skill.run"
+ "network.machines.manage"
]
]
}
}
],
],
@@ -737,7 +698,7 @@
@@ -737,7 +698,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/actor.skill-run-request.v1"
+ "$ref": "#/components/schemas/bot.machine-manage-request.v1"
}
}
}
}
}
}
@@ -758,7 +719,7 @@
@@ -758,7 +719,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/actor.skill-run-result.v1"
+ "$ref": "#/components/schemas/network.machine-manage-result.v1"
}
}
}
}
}
}
@@ -776,112 +737,46 @@
@@ -776,112 +737,46 @@
}
}
}
}
},
},
- "/api/v1/me/consent": {
- "get": {
- "operationId": "get_api_v1_me_consent",
- "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
+ "/api/v1/machines/{id}/roles": {
+ "put": {
+ "operationId": "put_api_v1_machines_id_roles",
+ "summary": "Pair, rename, give roles to and revoke a person's own machines.",
+ "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.",
"tags": [
"tags": [
- "network.consent"
+ "network.machines"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.consent.manage"
+ "network.machines.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
"public"
"public"
],
],
- "x-openvibe-quota-class": "standard",
+ "x-openvibe-quota-class": "network-write",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.consent.manage"
+ "network.machines.manage"
]
]
}
}
],
],
- "x-openvibe-input": {
- "$ref": "#/components/schemas/network.consent-choice.v1"
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/bot.machine-manage-request.v1"
+ }
+ }
+ }
},
},
"parameters": [
"parameters": [
{
{
- "name": "on",
- "in": "query",
+ "name": "id",
+ "in": "path",
"required": true,
"required": true,
"schema": {
"schema": {
- "type": "boolean"
- }
- },
- {
- "name": "kinds",
- "in": "query",
- "required": false,
- "schema": {
- "type": "array",
- "maxItems": 8,
- "uniqueItems": true,
- "items": {
- "enum": [
- "streams",
- "vods",
- "clips",
- "voice",
- "chat",
- "posts",
- "pictures",
- "code"
- ]
- }
- }
- },
- {
- "name": "options",
- "in": "query",
- "required": false,
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "licence": {
- "enum": [
- "cc-by-4.0",
- "cc0-1.0"
- ]
- },
- "features": {
- "type": "array",
- "maxItems": 4,
- "uniqueItems": true,
- "items": {
- "enum": [
- "verify",
- "find",
- "cast",
- "library"
- ]
- }
- }
- }
+ "type": "string"
}
}
- },
- {
- "name": "text_version",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string",
- "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",
- "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
- },
- "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
- },
- {
- "name": "adult",
- "in": "query",
- "required": false,
- "schema": {
- "type": "boolean",
- "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
- },
- "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
}
}
],
],
"responses": {
"responses": {
@@ -890,7 +785,7 @@
@@ -890,7 +785,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/network.me-consent-result.v1"
+ "$ref": "#/components/schemas/network.machine-manage-result.v1"
}
}
}
}
}
}
@@ -908,25 +803,25 @@
@@ -908,25 +803,25 @@
}
}
}
}
},
},
- "/api/v1/me/consent/{purpose}": {
- "put": {
- "operationId": "put_api_v1_me_consent_purpose",
- "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
+ "/api/v1/machines/pairings": {
+ "post": {
+ "operationId": "post_api_v1_machines_pairings",
+ "summary": "Pair, rename, give roles to and revoke a person's own machines.",
+ "description": "**network.machines.manage** (public): Pair, rename, give roles to and revoke a person's own machines. Bot stays the registry underneath: Network asks Bot as the person, and Bot checks that the machine is theirs. An app needs the person's consent for this scope, and may then do only what the person could.",
"tags": [
"tags": [
- "network.consent"
+ "network.machines"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.consent.manage"
+ "network.machines.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
"public"
"public"
],
],
- "x-openvibe-quota-class": "standard",
+ "x-openvibe-quota-class": "network-write",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.consent.manage"
+ "network.machines.manage"
]
]
}
}
],
],
@@ -935,28 +830,18 @@
@@ -935,28 +830,18 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/network.consent-choice.v1"
+ "$ref": "#/components/schemas/bot.machine-manage-request.v1"
}
}
}
}
}
}
},
},
- "parameters": [
- {
- "name": "purpose",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
"responses": {
"responses": {
"2XX": {
"2XX": {
"description": "Success",
"description": "Success",
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/network.me-consent-result.v1"
+ "$ref": "#/components/schemas/network.machine-manage-result.v1"
}
}
}
}
}
}
@@ -974,128 +859,72 @@
@@ -974,128 +859,72 @@
}
}
}
}
},
},
- "/api/v1/me/consent/{purpose}/{client}": {
- "delete": {
- "operationId": "delete_api_v1_me_consent_purpose_client",
- "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
+ "/api/v1/machines/roles": {
+ "get": {
+ "operationId": "get_api_v1_machines_roles",
+ "summary": "Read a person's own machines: the list, one machine, and the catalogue of roles.",
+ "description": "**network.machines.read** (public): Read a person's own machines: the list, one machine, and the catalogue of roles. A person reads their own with their own token; an app needs the person's consent for this scope (it is a public capability, so it can be granted to an app, and the app then acts only on behalf of the person who consented).",
"tags": [
"tags": [
- "network.consent"
+ "network.machines"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.consent.manage"
+ "network.machines.read"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
"public"
"public"
],
],
- "x-openvibe-quota-class": "standard",
+ "x-openvibe-quota-class": "network-read",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.consent.manage"
+ "network.machines.read"
]
]
}
}
],
],
- "x-openvibe-input": {
- "$ref": "#/components/schemas/network.consent-choice.v1"
- },
- "parameters": [
- {
- "name": "purpose",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/network.machine-list-result.v1"
+ }
+ }
}
}
},
},
- {
- "name": "client",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
}
}
- },
+ }
+ }
+ }
+ },
+ "/api/v1/me/actions": {
+ "get": {
+ "operationId": "get_api_v1_me_actions",
+ "summary": "The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and f",
+ "description": "**network.actions.read** (public): The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and failed (network.me-actions-result@1); POST /api/v1/me/actions/:service/:id/:action approves, declines, cancels or retries through the owning authority. Readable from every OpenVibe site with the person's Network token, like the bell.",
+ "tags": [
+ "network.actions"
+ ],
+ "x-openvibe-capabilities": [
+ "network.actions.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
{
{
- "name": "on",
- "in": "query",
- "required": true,
- "schema": {
- "type": "boolean"
- }
- },
- {
- "name": "kinds",
- "in": "query",
- "required": false,
- "schema": {
- "type": "array",
- "maxItems": 8,
- "uniqueItems": true,
- "items": {
- "enum": [
- "streams",
- "vods",
- "clips",
- "voice",
- "chat",
- "posts",
- "pictures",
- "code"
- ]
- }
- }
- },
- {
- "name": "options",
- "in": "query",
- "required": false,
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "licence": {
- "enum": [
- "cc-by-4.0",
- "cc0-1.0"
- ]
- },
- "features": {
- "type": "array",
- "maxItems": 4,
- "uniqueItems": true,
- "items": {
- "enum": [
- "verify",
- "find",
- "cast",
- "library"
- ]
- }
- }
- }
- }
- },
- {
- "name": "text_version",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string",
- "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",
- "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
- },
- "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
- },
- {
- "name": "adult",
- "in": "query",
- "required": false,
- "schema": {
- "type": "boolean",
- "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
- },
- "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
+ "openvibeToken": [
+ "network.actions.read"
+ ]
}
}
],
],
"responses": {
"responses": {
@@ -1104,7 +933,7 @@
@@ -1104,7 +933,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/network.me-consent-result.v1"
+ "$ref": "#/components/schemas/network.me-actions-result.v1"
}
}
}
}
}
}
@@ -1122,16 +951,16 @@
@@ -1122,16 +951,16 @@
}
}
}
}
},
},
- "/api/v1/me/consent/{purpose}/{client}/refusal": {
- "delete": {
- "operationId": "delete_api_v1_me_consent_purpose_client_refusal",
- "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
- "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
+ "/api/v1/me/actions/{service}/{id}/{action}": {
+ "post": {
+ "operationId": "post_api_v1_me_actions_service_id_action",
+ "summary": "The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and f",
+ "description": "**network.actions.read** (public): The Action Center for the signed-in person (plan 2026-10-28 §3.10): GET /api/v1/me/actions merges Actor, Codes and AI items and Network's own confirmations into running, waiting, scheduled, done and failed (network.me-actions-result@1); POST /api/v1/me/actions/:service/:id/:action approves, declines, cancels or retries through the owning authority. Readable from every OpenVibe site with the person's Network token, like the bell.",
"tags": [
"tags": [
- "network.consent"
+ "network.actions"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.consent.manage"
+ "network.actions.read"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
"public"
"public"
@@ -1140,16 +969,13 @@
@@ -1140,16 +969,13 @@
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.consent.manage"
+ "network.actions.read"
]
]
}
}
],
],
- "x-openvibe-input": {
- "$ref": "#/components/schemas/network.consent-choice.v1"
- },
"parameters": [
"parameters": [
{
{
- "name": "purpose",
+ "name": "service",
"in": "path",
"in": "path",
"required": true,
"required": true,
"schema": {
"schema": {
@@ -1157,7 +983,7 @@
@@ -1157,7 +983,7 @@
}
}
},
},
{
{
- "name": "client",
+ "name": "id",
"in": "path",
"in": "path",
"required": true,
"required": true,
"schema": {
"schema": {
@@ -1165,85 +991,78 @@
@@ -1165,85 +991,78 @@
}
}
},
},
{
{
- "name": "on",
- "in": "query",
+ "name": "action",
+ "in": "path",
"required": true,
"required": true,
"schema": {
"schema": {
- "type": "boolean"
+ "type": "string"
}
}
- },
- {
- "name": "kinds",
- "in": "query",
- "required": false,
- "schema": {
- "type": "array",
- "maxItems": 8,
- "uniqueItems": true,
- "items": {
- "enum": [
- "streams",
- "vods",
- "clips",
- "voice",
- "chat",
- "posts",
- "pictures",
- "code"
- ]
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/network.me-actions-result.v1"
+ }
}
}
}
}
},
},
- {
- "name": "options",
- "in": "query",
- "required": false,
- "schema": {
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "licence": {
- "enum": [
- "cc-by-4.0",
- "cc0-1.0"
- ]
- },
- "features": {
- "type": "array",
- "maxItems": 4,
- "uniqueItems": true,
- "items": {
- "enum": [
- "verify",
- "find",
- "cast",
- "library"
- ]
- }
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
}
}
}
}
}
}
- },
+ }
+ }
+ }
+ },
+ "/api/v1/me/actions/skills/{id}/run": {
+ "post": {
+ "operationId": "post_api_v1_me_actions_skills_id_run",
+ "summary": "Start one of Actor's catalogue skills as the signed-in person (plan 2026-11-09 §7): POST /api/v1/me/actions/skills/:id/run asks Actor to run that skill for them, with the input and the choice.",
+ "description": "**network.skill.run** (public): Start one of Actor's catalogue skills as the signed-in person (plan 2026-11-09 §7): POST /api/v1/me/actions/skills/:id/run asks Actor to run that skill for them, with the input and the choice. Network never runs the skill itself; Actor does, under the person's own approvals and budget.",
+ "tags": [
+ "network.skill"
+ ],
+ "x-openvibe-capabilities": [
+ "network.skill.run"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
{
{
- "name": "text_version",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string",
- "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",
- "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
- },
- "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
- },
+ "openvibeToken": [
+ "network.skill.run"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/actor.skill-run-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
{
{
- "name": "adult",
- "in": "query",
- "required": false,
+ "name": "id",
+ "in": "path",
+ "required": true,
"schema": {
"schema": {
- "type": "boolean",
- "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
- },
- "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
+ "type": "string"
+ }
}
}
],
],
"responses": {
"responses": {
@@ -1252,7 +1071,7 @@
@@ -1252,7 +1071,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/network.me-consent-result.v1"
+ "$ref": "#/components/schemas/actor.skill-run-result.v1"
}
}
}
}
}
}
@@ -1270,9 +1089,9 @@
@@ -1270,9 +1089,9 @@
}
}
}
}
},
},
- "/api/v1/me/consent/apps": {
+ "/api/v1/me/consent": {
"get": {
"get": {
- "operationId": "get_api_v1_me_consent_apps",
+ "operationId": "get_api_v1_me_consent",
"summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
"summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
"description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
"description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
"tags": [
"tags": [
@@ -1402,38 +1221,55 @@
@@ -1402,38 +1221,55 @@
}
}
}
}
},
},
- "/api/v1/me/emails": {
- "get": {
- "operationId": "get_api_v1_me_emails",
- "summary": "Read the one verified email address a person has shared with this app, or check whether one supplied address is verified for a supplied subject.",
- "description": "**identity.emails.read** (public): Read the one verified email address a person has shared with this app, or check whether one supplied address is verified for a supplied subject. The full address list is only available to the person in their own account hub.",
+ "/api/v1/me/consent/{purpose}": {
+ "put": {
+ "operationId": "put_api_v1_me_consent_purpose",
+ "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
+ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
"tags": [
"tags": [
- "identity.emails"
+ "network.consent"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "identity.emails.read"
+ "network.consent.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
"public"
"public"
],
],
- "x-openvibe-quota-class": "identity-read",
+ "x-openvibe-quota-class": "standard",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "identity.emails.read"
+ "network.consent.manage"
]
]
}
}
],
],
- "x-openvibe-input": {
- "$ref": "#/components/schemas/identity.emails-request.v1"
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/network.consent-choice.v1"
+ }
+ }
+ }
},
},
+ "parameters": [
+ {
+ "name": "purpose",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
"responses": {
"responses": {
"2XX": {
"2XX": {
"description": "Success",
"description": "Success",
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/identity.emails.v1"
+ "$ref": "#/components/schemas/network.me-consent-result.v1"
}
}
}
}
}
}
@@ -1451,119 +1287,140 @@
@@ -1451,119 +1287,140 @@
}
}
}
}
},
},
- "/api/v1/me/trust": {
- "get": {
- "operationId": "get_api_v1_me_trust",
- "summary": "Read privacy-limited trust signals for an account or guest by Network subject.",
- "description": "**identity.trust.read** (public): Read privacy-limited trust signals for an account or guest by Network subject. The caller receives only age bands, booleans and aggregate counts, never an IP, email, last-seen timestamp, service user id or linked account identities. Services may use the capability; an app needs the owner's explicit capability grant.",
+ "/api/v1/me/consent/{purpose}/{client}": {
+ "delete": {
+ "operationId": "delete_api_v1_me_consent_purpose_client",
+ "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
+ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
"tags": [
"tags": [
- "identity.trust"
+ "network.consent"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "identity.trust.read"
+ "network.consent.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
"public"
"public"
],
],
- "x-openvibe-quota-class": "identity-read",
+ "x-openvibe-quota-class": "standard",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "identity.trust.read"
+ "network.consent.manage"
]
]
}
}
],
],
"x-openvibe-input": {
"x-openvibe-input": {
- "$ref": "#/components/schemas/identity.trust-request.v1"
+ "$ref": "#/components/schemas/network.consent-choice.v1"
},
},
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/identity.trust.v1"
- }
+ "parameters": [
+ {
+ "name": "purpose",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "client",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "on",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "name": "kinds",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 8,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "streams",
+ "vods",
+ "clips",
+ "voice",
+ "chat",
+ "posts",
+ "pictures",
+ "code"
+ ]
}
}
}
}
},
},
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
+ {
+ "name": "options",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "licence": {
+ "enum": [
+ "cc-by-4.0",
+ "cc0-1.0"
+ ]
+ },
+ "features": {
+ "type": "array",
+ "maxItems": 4,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "verify",
+ "find",
+ "cast",
+ "library"
+ ]
+ }
}
}
}
}
}
}
- }
- }
- }
- },
- "/api/v1/node/self/capabilities": {
- "put": {
- "operationId": "put_api_v1_node_self_capabilities",
- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
- "tags": [
- "network.node"
- ],
- "x-openvibe-capabilities": [
- "network.node.self.manage"
- ],
- "x-openvibe-visibility": [
- "internal"
- ],
- "x-openvibe-quota-class": "admin",
- "security": [
+ },
{
{
- "openvibeToken": [
- "network.node.self.manage"
- ]
+ "name": "text_version",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",
+ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
+ },
+ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
+ },
+ {
+ "name": "adult",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
+ },
+ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
}
}
],
],
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success"
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
+ "description": "Success",
"content": {
"content": {
- "application/problem+json": {
+ "application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
+ "$ref": "#/components/schemas/network.me-consent-result.v1"
}
}
}
}
}
}
- }
- }
- }
- },
- "/api/v1/node/self/credential": {
- "post": {
- "operationId": "post_api_v1_node_self_credential",
- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
- "tags": [
- "network.node"
- ],
- "x-openvibe-capabilities": [
- "network.node.self.manage"
- ],
- "x-openvibe-visibility": [
- "internal"
- ],
- "x-openvibe-quota-class": "admin",
- "security": [
- {
- "openvibeToken": [
- "network.node.self.manage"
- ]
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success"
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -1578,41 +1435,140 @@
@@ -1578,41 +1435,140 @@
}
}
}
}
},
},
- "/api/v1/node/self/pools/{id}": {
- "get": {
- "operationId": "get_api_v1_node_self_pools_id",
- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
+ "/api/v1/me/consent/{purpose}/{client}/refusal": {
+ "delete": {
+ "operationId": "delete_api_v1_me_consent_purpose_client_refusal",
+ "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
+ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
"tags": [
"tags": [
- "network.node"
+ "network.consent"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.node.self.manage"
+ "network.consent.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "internal"
+ "public"
],
],
- "x-openvibe-quota-class": "admin",
+ "x-openvibe-quota-class": "standard",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.node.self.manage"
+ "network.consent.manage"
]
]
}
}
],
],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/network.consent-choice.v1"
+ },
"parameters": [
"parameters": [
{
{
- "name": "id",
+ "name": "purpose",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "client",
"in": "path",
"in": "path",
"required": true,
"required": true,
"schema": {
"schema": {
"type": "string"
"type": "string"
}
}
+ },
+ {
+ "name": "on",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "name": "kinds",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 8,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "streams",
+ "vods",
+ "clips",
+ "voice",
+ "chat",
+ "posts",
+ "pictures",
+ "code"
+ ]
+ }
+ }
+ },
+ {
+ "name": "options",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "licence": {
+ "enum": [
+ "cc-by-4.0",
+ "cc0-1.0"
+ ]
+ },
+ "features": {
+ "type": "array",
+ "maxItems": 4,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "verify",
+ "find",
+ "cast",
+ "library"
+ ]
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "text_version",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",
+ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
+ },
+ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
+ },
+ {
+ "name": "adult",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
+ },
+ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
}
}
],
],
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success"
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/network.me-consent-result.v1"
+ }
+ }
+ }
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -1627,90 +1583,124 @@
@@ -1627,90 +1583,124 @@
}
}
}
}
},
},
- "/api/v1/node/self/pools/{id}/approve": {
- "post": {
- "operationId": "post_api_v1_node_self_pools_id_approve",
- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
+ "/api/v1/me/consent/apps": {
+ "get": {
+ "operationId": "get_api_v1_me_consent_apps",
+ "summary": "\"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or",
+ "description": "**network.consent.manage** (public): \"Your data choices\" for the signed-in person (L24): GET /api/v1/me/consent lists every purpose with its wording, the person's choices and their history; PUT /api/v1/me/consent/:purpose turns one on or off (network.consent-choice@1); DELETE /api/v1/me/consent/:purpose/:client turns one app's purpose off. \"Apps you allowed\" (0.310.0): GET /api/v1/me/consent/apps lists the purposes each app was granted or refused at sign-in (network.me-consent-apps-result@1), and DELETE /api/v1/me/consent/:purpose/:client/refusal ends a refusal's 30 quiet days so that app's next sign-in asks again. Off is the default for every purpose. Only the person's own session or token reaches it.",
"tags": [
"tags": [
- "network.node"
+ "network.consent"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.node.self.manage"
+ "network.consent.manage"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "internal"
+ "public"
],
],
- "x-openvibe-quota-class": "admin",
+ "x-openvibe-quota-class": "standard",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.node.self.manage"
+ "network.consent.manage"
]
]
}
}
],
],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/network.consent-choice.v1"
+ },
"parameters": [
"parameters": [
{
{
- "name": "id",
- "in": "path",
+ "name": "on",
+ "in": "query",
"required": true,
"required": true,
"schema": {
"schema": {
- "type": "string"
+ "type": "boolean"
}
}
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success"
},
},
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
+ {
+ "name": "kinds",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "maxItems": 8,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "streams",
+ "vods",
+ "clips",
+ "voice",
+ "chat",
+ "posts",
+ "pictures",
+ "code"
+ ]
+ }
+ }
+ },
+ {
+ "name": "options",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "licence": {
+ "enum": [
+ "cc-by-4.0",
+ "cc0-1.0"
+ ]
+ },
+ "features": {
+ "type": "array",
+ "maxItems": 4,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "verify",
+ "find",
+ "cast",
+ "library"
+ ]
+ }
}
}
}
}
}
}
- }
- }
- }
- },
- "/api/v1/node/self/pools/{id}/leave": {
- "post": {
- "operationId": "post_api_v1_node_self_pools_id_leave",
- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
- "tags": [
- "network.node"
- ],
- "x-openvibe-capabilities": [
- "network.node.self.manage"
- ],
- "x-openvibe-visibility": [
- "internal"
- ],
- "x-openvibe-quota-class": "admin",
- "security": [
+ },
{
{
- "openvibeToken": [
- "network.node.self.manage"
- ]
- }
- ],
- "parameters": [
+ "name": "text_version",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{1,3}$",
+ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
+ },
+ "description": "The wording the page showed. Required to turn on; a stale one is refused (consent.stale_text) so the page shows the new words first."
+ },
{
{
- "name": "id",
- "in": "path",
- "required": true,
+ "name": "adult",
+ "in": "query",
+ "required": false,
"schema": {
"schema": {
- "type": "string"
- }
+ "type": "boolean",
+ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
+ },
+ "description": "The person confirmed they are 18 or older. Required (true) to turn on face.recognize, ai.looks, ai.train or data.public."
}
}
],
],
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success"
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/network.me-consent-result.v1"
+ }
+ }
+ }
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -1725,41 +1715,41 @@
@@ -1725,41 +1715,41 @@
}
}
}
}
},
},
- "/api/v1/node/self/pools/{id}/status": {
- "post": {
- "operationId": "post_api_v1_node_self_pools_id_status",
- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
+ "/api/v1/me/emails": {
+ "get": {
+ "operationId": "get_api_v1_me_emails",
+ "summary": "Read the one verified email address a person has shared with this app, or check whether one supplied address is verified for a supplied subject.",
+ "description": "**identity.emails.read** (public): Read the one verified email address a person has shared with this app, or check whether one supplied address is verified for a supplied subject. The full address list is only available to the person in their own account hub.",
"tags": [
"tags": [
- "network.node"
+ "identity.emails"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.node.self.manage"
+ "identity.emails.read"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "internal"
+ "public"
],
],
- "x-openvibe-quota-class": "admin",
+ "x-openvibe-quota-class": "identity-read",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.node.self.manage"
+ "identity.emails.read"
]
]
}
}
],
],
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/identity.emails-request.v1"
+ },
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success"
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/identity.emails.v1"
+ }
+ }
+ }
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -1774,31 +1764,41 @@
@@ -1774,31 +1764,41 @@
}
}
}
}
},
},
- "/api/v1/node/self/pools/announce": {
- "post": {
- "operationId": "post_api_v1_node_self_pools_announce",
- "summary": "A paired machine presents its own capabilities and rotates its own credential, never another machine's.",
- "description": "**network.node.self.manage** (internal): A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
+ "/api/v1/me/trust": {
+ "get": {
+ "operationId": "get_api_v1_me_trust",
+ "summary": "Read privacy-limited trust signals for an account or guest by Network subject.",
+ "description": "**identity.trust.read** (public): Read privacy-limited trust signals for an account or guest by Network subject. The caller receives only age bands, booleans and aggregate counts, never an IP, email, last-seen timestamp, service user id or linked account identities. Services may use the capability; an app needs the owner's explicit capability grant.",
"tags": [
"tags": [
- "network.node"
+ "identity.trust"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.node.self.manage"
+ "identity.trust.read"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "internal"
+ "public"
],
],
- "x-openvibe-quota-class": "admin",
+ "x-openvibe-quota-class": "identity-read",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.node.self.manage"
+ "identity.trust.read"
]
]
}
}
],
],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/identity.trust-request.v1"
+ },
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success"
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/identity.trust.v1"
+ }
+ }
+ }
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -2925,35 +2925,228 @@
@@ -2925,35 +2925,228 @@
}
}
}
}
},
},
- "/internal/integrations/github-token": {
- "get": {
- "operationId": "get_internal_integrations_github_token",
- "summary": "Read the network's GitHub API token (read-only, public repositories), which the owner sets in Network's admin panel or in GITHUB_TOKEN.",
- "description": "**network.integration.github.read** (first-party): Read the network's GitHub API token (read-only, public repositories), which the owner sets in Network's admin panel or in GITHUB_TOKEN. Used by OpenVibe.Blog's network changelog so its GitHub calls do not share the host's anonymous rate limit. Never granted to apps or mods.",
+ "/internal/integrations/github-token": {
+ "get": {
+ "operationId": "get_internal_integrations_github_token",
+ "summary": "Read the network's GitHub API token (read-only, public repositories), which the owner sets in Network's admin panel or in GITHUB_TOKEN.",
+ "description": "**network.integration.github.read** (first-party): Read the network's GitHub API token (read-only, public repositories), which the owner sets in Network's admin panel or in GITHUB_TOKEN. Used by OpenVibe.Blog's network changelog so its GitHub calls do not share the host's anonymous rate limit. Never granted to apps or mods.",
+ "tags": [
+ "network.integration"
+ ],
+ "x-openvibe-capabilities": [
+ "network.integration.github.read"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "integration-secret",
+ "security": [
+ {
+ "openvibeToken": [
+ "network.integration.github.read"
+ ]
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/network.github-token-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/link-pairings": {
+ "post": {
+ "operationId": "post_internal_link_pairings",
+ "summary": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired.",
+ "description": "**network.link.manage** (internal): A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",
+ "tags": [
+ "network.link"
+ ],
+ "x-openvibe-capabilities": [
+ "network.link.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "admin",
+ "security": [
+ {
+ "openvibeToken": [
+ "network.link.manage"
+ ]
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/link-principals/{id}": {
+ "get": {
+ "operationId": "get_internal_link_principals_id",
+ "summary": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired.",
+ "description": "**network.link.manage** (internal): A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",
+ "tags": [
+ "network.link"
+ ],
+ "x-openvibe-capabilities": [
+ "network.link.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "admin",
+ "security": [
+ {
+ "openvibeToken": [
+ "network.link.manage"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/link-principals/{id}/revoke": {
+ "post": {
+ "operationId": "post_internal_link_principals_id_revoke",
+ "summary": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired.",
+ "description": "**network.link.manage** (internal): A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",
+ "tags": [
+ "network.link"
+ ],
+ "x-openvibe-capabilities": [
+ "network.link.manage"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "admin",
+ "security": [
+ {
+ "openvibeToken": [
+ "network.link.manage"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success"
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/internal/links/report": {
+ "post": {
+ "operationId": "post_internal_links_report",
+ "summary": "Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",
+ "description": "**network.link.report** (internal): Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",
"tags": [
"tags": [
- "network.integration"
+ "network.link"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.integration.github.read"
+ "network.link.report"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "first-party"
+ "internal"
],
],
- "x-openvibe-quota-class": "integration-secret",
+ "x-openvibe-quota-class": "admin",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.integration.github.read"
+ "network.link.report"
]
]
}
}
],
],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/network.link-report-request.v1"
+ }
+ }
+ }
+ },
"responses": {
"responses": {
"2XX": {
"2XX": {
"description": "Success",
"description": "Success",
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/network.github-token-result.v1"
+ "$ref": "#/components/schemas/network.link-list-result.v1"
}
}
}
}
}
}
@@ -3299,248 +3492,17 @@
@@ -3299,248 +3492,17 @@
],
],
"requestBody": {
"requestBody": {
"required": false,
"required": false,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/network.mod-install-request.v1"
- }
- }
- }
- },
- "parameters": [
- {
- "name": "mod_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/network.mod-principal.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/internal/modules/{namespace}/{subject}": {
- "get": {
- "operationId": "get_internal_modules_namespace_subject",
- "summary": "Read a subject's user-module record in a granted namespace.",
- "description": "**network.modules.read** (first-party): Read a subject's user-module record in a granted namespace.",
- "tags": [
- "network.modules"
- ],
- "x-openvibe-capabilities": [
- "network.modules.read"
- ],
- "x-openvibe-visibility": [
- "first-party"
- ],
- "x-openvibe-quota-class": "modules-read",
- "security": [
- {
- "openvibeToken": [
- "network.modules.read"
- ]
- }
- ],
- "parameters": [
- {
- "name": "namespace",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "subject",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/modules.module-record.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- },
- "put": {
- "operationId": "put_internal_modules_namespace_subject",
- "summary": "Write a subject's record in a namespace the service owns (If-Match revision).",
- "description": "**network.modules.write** (first-party): Write a subject's record in a namespace the service owns (If-Match revision).",
- "tags": [
- "network.modules"
- ],
- "x-openvibe-capabilities": [
- "network.modules.write"
- ],
- "x-openvibe-visibility": [
- "first-party"
- ],
- "x-openvibe-quota-class": "modules-write",
- "security": [
- {
- "openvibeToken": [
- "network.modules.write"
- ]
- }
- ],
- "requestBody": {
- "required": false,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/network.module-write-request.v1"
- }
- }
- }
- },
- "parameters": [
- {
- "name": "namespace",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "subject",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/modules.module-record.v1"
- }
- }
- }
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/internal/node-pairings": {
- "post": {
- "operationId": "post_internal_node_pairings",
- "summary": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired.",
- "description": "**network.node.manage** (internal): A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",
- "tags": [
- "network.node"
- ],
- "x-openvibe-capabilities": [
- "network.node.manage"
- ],
- "x-openvibe-visibility": [
- "internal"
- ],
- "x-openvibe-quota-class": "admin",
- "security": [
- {
- "openvibeToken": [
- "network.node.manage"
- ]
- }
- ],
- "responses": {
- "2XX": {
- "description": "Success"
- },
- "default": {
- "description": "An error (RFC 9457 problem details)",
- "content": {
- "application/problem+json": {
- "schema": {
- "$ref": "#/components/schemas/errors.problem.v1"
- }
- }
- }
- }
- }
- }
- },
- "/internal/node-principals/{id}": {
- "get": {
- "operationId": "get_internal_node_principals_id",
- "summary": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired.",
- "description": "**network.node.manage** (internal): A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",
- "tags": [
- "network.node"
- ],
- "x-openvibe-capabilities": [
- "network.node.manage"
- ],
- "x-openvibe-visibility": [
- "internal"
- ],
- "x-openvibe-quota-class": "admin",
- "security": [
- {
- "openvibeToken": [
- "network.node.manage"
- ]
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/network.mod-install-request.v1"
+ }
+ }
}
}
- ],
+ },
"parameters": [
"parameters": [
{
{
- "name": "id",
+ "name": "mod_id",
"in": "path",
"in": "path",
"required": true,
"required": true,
"schema": {
"schema": {
@@ -3550,7 +3512,14 @@
@@ -3550,7 +3512,14 @@
],
],
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success"
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/network.mod-principal.v1"
+ }
+ }
+ }
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -3565,31 +3534,39 @@
@@ -3565,31 +3534,39 @@
}
}
}
}
},
},
- "/internal/node-principals/{id}/revoke": {
- "post": {
- "operationId": "post_internal_node_principals_id_revoke",
- "summary": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired.",
- "description": "**network.node.manage** (internal): A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",
+ "/internal/modules/{namespace}/{subject}": {
+ "get": {
+ "operationId": "get_internal_modules_namespace_subject",
+ "summary": "Read a subject's user-module record in a granted namespace.",
+ "description": "**network.modules.read** (first-party): Read a subject's user-module record in a granted namespace.",
"tags": [
"tags": [
- "network.node"
+ "network.modules"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.node.manage"
+ "network.modules.read"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "internal"
+ "first-party"
],
],
- "x-openvibe-quota-class": "admin",
+ "x-openvibe-quota-class": "modules-read",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.node.manage"
+ "network.modules.read"
]
]
}
}
],
],
"parameters": [
"parameters": [
{
{
- "name": "id",
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "subject",
"in": "path",
"in": "path",
"required": true,
"required": true,
"schema": {
"schema": {
@@ -3599,7 +3576,14 @@
@@ -3599,7 +3576,14 @@
],
],
"responses": {
"responses": {
"2XX": {
"2XX": {
- "description": "Success"
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/modules.module-record.v1"
+ }
+ }
+ }
},
},
"default": {
"default": {
"description": "An error (RFC 9457 problem details)",
"description": "An error (RFC 9457 problem details)",
@@ -3612,27 +3596,25 @@
@@ -3612,27 +3596,25 @@
}
}
}
}
}
}
- }
- },
- "/internal/nodes/report": {
- "post": {
- "operationId": "post_internal_nodes_report",
- "summary": "Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",
- "description": "**network.node.report** (internal): Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",
+ },
+ "put": {
+ "operationId": "put_internal_modules_namespace_subject",
+ "summary": "Write a subject's record in a namespace the service owns (If-Match revision).",
+ "description": "**network.modules.write** (first-party): Write a subject's record in a namespace the service owns (If-Match revision).",
"tags": [
"tags": [
- "network.node"
+ "network.modules"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.node.report"
+ "network.modules.write"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
- "internal"
+ "first-party"
],
],
- "x-openvibe-quota-class": "admin",
+ "x-openvibe-quota-class": "modules-write",
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.node.report"
+ "network.modules.write"
]
]
}
}
],
],
@@ -3641,18 +3623,36 @@
@@ -3641,18 +3623,36 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/network.node-report-request.v1"
+ "$ref": "#/components/schemas/network.module-write-request.v1"
}
}
}
}
}
}
},
},
+ "parameters": [
+ {
+ "name": "namespace",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "subject",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
"responses": {
"responses": {
"2XX": {
"2XX": {
"description": "Success",
"description": "Success",
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/network.node-list-result.v1"
+ "$ref": "#/components/schemas/modules.module-record.v1"
}
}
}
}
}
}
@@ -3952,12 +3952,12 @@
@@ -3952,12 +3952,12 @@
"post": {
"post": {
"operationId": "post_internal_registry_instances_report",
"operationId": "post_internal_registry_instances_report",
"summary": "Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",
"summary": "Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",
- "description": "**network.node.report** (internal): Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",
+ "description": "**network.link.report** (internal): Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",
"tags": [
"tags": [
- "network.node"
+ "network.link"
],
],
"x-openvibe-capabilities": [
"x-openvibe-capabilities": [
- "network.node.report"
+ "network.link.report"
],
],
"x-openvibe-visibility": [
"x-openvibe-visibility": [
"internal"
"internal"
@@ -3966,7 +3966,7 @@
@@ -3966,7 +3966,7 @@
"security": [
"security": [
{
{
"openvibeToken": [
"openvibeToken": [
- "network.node.report"
+ "network.link.report"
]
]
}
}
],
],
@@ -3975,7 +3975,7 @@
@@ -3975,7 +3975,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/network.node-report-request.v1"
+ "$ref": "#/components/schemas/network.link-report-request.v1"
}
}
}
}
}
}
@@ -3986,7 +3986,7 @@
@@ -3986,7 +3986,7 @@
"content": {
"content": {
"application/json": {
"application/json": {
"schema": {
"schema": {
- "$ref": "#/components/schemas/network.node-list-result.v1"
+ "$ref": "#/components/schemas/network.link-list-result.v1"
}
}
}
}
}
}
@@ -6660,73 +6660,236 @@
@@ -6660,73 +6660,236 @@
"channel"
"channel"
]
]
},
},
- "target_id": {
+ "target_id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "followers": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "following": {
+ "type": "boolean",
+ "description": "Present for a signed-in caller."
+ },
+ "notify_email": {
+ "type": "boolean"
+ },
+ "notify_push": {
+ "type": "boolean"
+ },
+ "since": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the caller started following."
+ }
+ }
+ },
+ "network.follow-write-request.v1": {
+ "title": "NetworkFollowWriteRequest",
+ "description": "network.follow-write-request@1 (ADR-030 step 4; capability network.follows.write): a first-party product records a follow or unfollow on a person's behalf, where they pressed the button. PUT /internal/follows/:type/:target { follower, notify_email?, notify_push? } follows (idempotent; flags change a follow). DELETE /internal/follows/:type/:target?follower=usr_… unfollows (idempotent). Answers network.follow-status-result@1 for the follower. Network emits network.follow.* as for a person's own change.",
+ "type": "object",
+ "required": [
+ "follower"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "follower": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "notify_email": {
+ "type": "boolean"
+ },
+ "notify_push": {
+ "type": "boolean"
+ }
+ }
+ },
+ "network.github-token-result.v1": {
+ "title": "NetworkGithubTokenResult",
+ "description": "network.github-token-result@1: the answer of network.integration.github.read on OpenVibe.Network. GET /internal/integrations/github-token (a service token holding the capability; the shared internal key is refused) → { token, source }: the network's read-only GitHub API token and where it is set (GITHUB_TOKEN in the environment, or the value the owner saved in admin). Answered with Cache-Control: no-store; 404 { error: not_configured, detail } when no token is set.",
+ "type": "object",
+ "required": [
+ "token",
+ "source"
+ ],
+ "properties": {
+ "token": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A GitHub token (ghp_…, github_pat_…). A secret: never log or show it."
+ },
+ "source": {
+ "enum": [
+ "env",
+ "database",
+ "unset"
+ ],
+ "description": "env: GITHUB_TOKEN; database: saved in admin (Settings → GitHub)."
+ }
+ }
+ },
+ "network.link-list-result.v1": {
+ "title": "NetworkLinkListResult",
+ "description": "network.link-list-result@1: GET /api/v1/links on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().",
+ "type": "object",
+ "required": [
+ "nodes",
+ "generated_at"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "nodes": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/network.link.v1"
+ }
+ },
+ "generated_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "network.link-report-request.v1": {
+ "title": "NetworkLinkReportRequest",
+ "description": "network.link-report-request@1: POST /internal/links/report on OpenVibe.Network (capability network.link.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.",
+ "type": "object",
+ "required": [
+ "source",
+ "nodes"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "source": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$",
+ "description": "The reporting inventory (e.g. the host id)."
+ },
+ "nodes": {
+ "type": "array",
+ "maxItems": 500,
+ "items": {
+ "$ref": "#/components/schemas/network.link.v1"
+ }
+ }
+ }
+ },
+ "network.link.v1": {
+ "title": "NetworkLink",
+ "description": "network.link@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.",
+ "type": "object",
+ "required": [
+ "id",
+ "roles",
+ "location",
+ "health",
+ "updated_at"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 80
+ },
+ "roles": {
+ "type": "array",
+ "minItems": 1,
+ "uniqueItems": true,
+ "items": {
+ "enum": [
+ "web",
+ "app",
+ "data",
+ "media-worker",
+ "ingest",
+ "edge-probe",
+ "edge-relay",
+ "edge-cache",
+ "gpu",
+ "staging",
+ "ci"
+ ]
+ }
+ },
+ "location": {
+ "type": "object",
+ "required": [
+ "region",
+ "country"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "region": {
+ "type": "string",
+ "pattern": "^[a-z]{2}-[a-z]+(-[0-9])?$",
+ "description": "e.g. us-west, eu-central"
+ },
+ "country": {
+ "type": "string",
+ "pattern": "^[A-Z]{2}$"
+ },
+ "city": {
+ "type": "string",
+ "maxLength": 80
+ },
+ "lat": {
+ "type": "number",
+ "minimum": -90,
+ "maximum": 90
+ },
+ "lon": {
+ "type": "number",
+ "minimum": -180,
+ "maximum": 180
+ }
+ }
+ },
+ "provider": {
"type": "string",
"type": "string",
- "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
- },
- "followers": {
- "type": "integer",
- "minimum": 0
- },
- "following": {
- "type": "boolean",
- "description": "Present for a signed-in caller."
- },
- "notify_email": {
- "type": "boolean"
- },
- "notify_push": {
- "type": "boolean"
+ "maxLength": 40
},
},
- "since": {
- "type": "string",
- "format": "date-time",
- "description": "When the caller started following."
- }
- }
- },
- "network.follow-write-request.v1": {
- "title": "NetworkFollowWriteRequest",
- "description": "network.follow-write-request@1 (ADR-030 step 4; capability network.follows.write): a first-party product records a follow or unfollow on a person's behalf, where they pressed the button. PUT /internal/follows/:type/:target { follower, notify_email?, notify_push? } follows (idempotent; flags change a follow). DELETE /internal/follows/:type/:target?follower=usr_… unfollows (idempotent). Answers network.follow-status-result@1 for the follower. Network emits network.follow.* as for a person's own change.",
- "type": "object",
- "required": [
- "follower"
- ],
- "additionalProperties": false,
- "properties": {
- "follower": {
+ "beacon": {
"type": "string",
"type": "string",
- "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ "format": "uri",
+ "pattern": "^https://",
+ "description": "Timed by clients (GET, tiny body, no caching) to measure round-trip time."
},
},
- "notify_email": {
- "type": "boolean"
+ "health": {
+ "type": "object",
+ "required": [
+ "status",
+ "checked_at"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "status": {
+ "enum": [
+ "up",
+ "degraded",
+ "down",
+ "unknown"
+ ]
+ },
+ "checked_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
},
},
- "notify_push": {
- "type": "boolean"
- }
- }
- },
- "network.github-token-result.v1": {
- "title": "NetworkGithubTokenResult",
- "description": "network.github-token-result@1: the answer of network.integration.github.read on OpenVibe.Network. GET /internal/integrations/github-token (a service token holding the capability; the shared internal key is refused) → { token, source }: the network's read-only GitHub API token and where it is set (GITHUB_TOKEN in the environment, or the value the owner saved in admin). Answered with Cache-Control: no-store; 404 { error: not_configured, detail } when no token is set.",
- "type": "object",
- "required": [
- "token",
- "source"
- ],
- "properties": {
- "token": {
+ "updated_at": {
"type": "string",
"type": "string",
- "minLength": 1,
- "description": "A GitHub token (ghp_…, github_pat_…). A secret: never log or show it."
+ "format": "date-time"
},
},
- "source": {
- "enum": [
- "env",
- "database",
- "unset"
- ],
- "description": "env: GITHUB_TOKEN; database: saved in admin (Settings → GitHub)."
+ "cell": {
+ "type": "string",
+ "pattern": "^[a-z]{2,8}-[0-9]{1,3}$",
+ "description": "Cell the node belongs to, e.g. wnam-1."
}
}
}
}
},
},
@@ -6781,8 +6944,8 @@
@@ -6781,8 +6944,8 @@
},
},
"principal_id": {
"principal_id": {
"type": "string",
"type": "string",
- "pattern": "^nod_[0-9A-HJKMNP-TV-Z]{26}$",
- "description": "The machine's node principal on Network (nod_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for."
+ "pattern": "^lnk_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The machine's node principal on Network (lnk_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for."
},
},
"name": {
"name": {
"type": "string",
"type": "string",
@@ -7286,169 +7449,6 @@
@@ -7286,169 +7449,6 @@
}
}
]
]
},
},
- "network.node-list-result.v1": {
- "title": "NetworkNodeListResult",
- "description": "network.node-list-result@1: GET /api/v1/nodes on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().",
- "type": "object",
- "required": [
- "nodes",
- "generated_at"
- ],
- "additionalProperties": false,
- "properties": {
- "nodes": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/network.node.v1"
- }
- },
- "generated_at": {
- "type": "string",
- "format": "date-time"
- }
- }
- },
- "network.node-report-request.v1": {
- "title": "NetworkNodeReportRequest",
- "description": "network.node-report-request@1: POST /internal/nodes/report on OpenVibe.Network (capability network.node.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.",
- "type": "object",
- "required": [
- "source",
- "nodes"
- ],
- "additionalProperties": false,
- "properties": {
- "source": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9-]{1,39}$",
- "description": "The reporting inventory (e.g. the host id)."
- },
- "nodes": {
- "type": "array",
- "maxItems": 500,
- "items": {
- "$ref": "#/components/schemas/network.node.v1"
- }
- }
- }
- },
- "network.node.v1": {
- "title": "NetworkNode",
- "description": "network.node@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.",
- "type": "object",
- "required": [
- "id",
- "roles",
- "location",
- "health",
- "updated_at"
- ],
- "additionalProperties": false,
- "properties": {
- "id": {
- "type": "string",
- "pattern": "^[a-z][a-z0-9-]{1,39}$"
- },
- "name": {
- "type": "string",
- "maxLength": 80
- },
- "roles": {
- "type": "array",
- "minItems": 1,
- "uniqueItems": true,
- "items": {
- "enum": [
- "web",
- "app",
- "data",
- "media-worker",
- "ingest",
- "edge-probe",
- "edge-relay",
- "edge-cache",
- "gpu",
- "staging",
- "ci"
- ]
- }
- },
- "location": {
- "type": "object",
- "required": [
- "region",
- "country"
- ],
- "additionalProperties": false,
- "properties": {
- "region": {
- "type": "string",
- "pattern": "^[a-z]{2}-[a-z]+(-[0-9])?$",
- "description": "e.g. us-west, eu-central"
- },
- "country": {
- "type": "string",
- "pattern": "^[A-Z]{2}$"
- },
- "city": {
- "type": "string",
- "maxLength": 80
- },
- "lat": {
- "type": "number",
- "minimum": -90,
- "maximum": 90
- },
- "lon": {
- "type": "number",
- "minimum": -180,
- "maximum": 180
- }
- }
- },
- "provider": {
- "type": "string",
- "maxLength": 40
- },
- "beacon": {
- "type": "string",
- "format": "uri",
- "pattern": "^https://",
- "description": "Timed by clients (GET, tiny body, no caching) to measure round-trip time."
- },
- "health": {
- "type": "object",
- "required": [
- "status",
- "checked_at"
- ],
- "additionalProperties": false,
- "properties": {
- "status": {
- "enum": [
- "up",
- "degraded",
- "down",
- "unknown"
- ]
- },
- "checked_at": {
- "type": "string",
- "format": "date-time"
- }
- }
- },
- "updated_at": {
- "type": "string",
- "format": "date-time"
- },
- "cell": {
- "type": "string",
- "pattern": "^[a-z]{2,8}-[0-9]{1,3}$",
- "description": "Cell the node belongs to, e.g. wnam-1."
- }
- }
- },
"network.notification-push-request.v1": {
"network.notification-push-request.v1": {
"title": "NetworkNotificationPushRequest",
"title": "NetworkNotificationPushRequest",
"description": "network.notification-push-request@1: bodies of network.notifications.push on OpenVibe.Network (a service token holding the capability). POST /internal/notifications/push: { user_id | subject, type?, title?, message?, … } creates one notification (400 without user_id or subject; 404 for an unknown subject). POST /internal/notifications/push-bulk: { user_ids | subjects, …same fields } creates it for each recipient (400 without a non-empty user_ids or subjects, or with more than 1000). Recipients are Network user ids or canonical usr_ subjects; a subject resolves to its account, so a service that knows the person by subject (Vision's consent request, Cam's event alert) need not hold the numeric id. Unknown fields are ignored; 503 when the notification service is not running. The other /internal/notifications/* routes (unread, mark-read, resolve-users) take the internal key only and are not part of this capability.",
"description": "network.notification-push-request@1: bodies of network.notifications.push on OpenVibe.Network (a service token holding the capability). POST /internal/notifications/push: { user_id | subject, type?, title?, message?, … } creates one notification (400 without user_id or subject; 404 for an unknown subject). POST /internal/notifications/push-bulk: { user_ids | subjects, …same fields } creates it for each recipient (400 without a non-empty user_ids or subjects, or with more than 1000). Recipients are Network user ids or canonical usr_ subjects; a subject resolves to its account, so a service that knows the person by subject (Vision's consent request, Cam's event alert) need not hold the numeric id. Unknown fields are ignored; 503 when the notification service is not running. The other /internal/notifications/* routes (unread, mark-read, resolve-users) take the internal key only and are not part of this capability.",
@@ -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.371.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.372.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": "OpenRestream API",
"title": "OpenRestream API",
- "version": "0.371.0",
- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Play API",
"title": "OpenVibe.Play API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Quest API",
"title": "OpenVibe.Quest API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Report API",
"title": "OpenVibe.Report API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Run API",
"title": "OpenVibe.Run API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"
@@ -1973,7 +1973,7 @@
@@ -1973,7 +1973,7 @@
"null"
"null"
],
],
"maxLength": 128,
"maxLength": 128,
- "description": "The offer's provider (the usage reading's `provider`); null for a first-party node."
+ "description": "The offer's provider (the usage reading's `provider`); null for a first-party machine."
},
},
"region": {
"region": {
"type": [
"type": [
@@ -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.371.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Services API",
"title": "OpenVibe.Services API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Today API",
"title": "OpenVibe.Today API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.371.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Vision API",
"title": "OpenVibe.Vision API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Watch API",
"title": "OpenVibe.Watch API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"
@@ -3006,7 +3006,7 @@
@@ -3006,7 +3006,7 @@
},
},
"watch.watch.v1": {
"watch.watch.v1": {
"title": "Watch",
"title": "Watch",
- "description": "watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Node probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.",
+ "description": "watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Link probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.",
"type": "object",
"type": "object",
"additionalProperties": false,
"additionalProperties": false,
"required": [
"required": [
@@ -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.371.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Work API",
"title": "OpenVibe.Work API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.372.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.Zone API",
"title": "OpenVibe.Zone API",
- "version": "0.371.0",
- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.371.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.372.0",
+ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.372.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"
@@ -76,7 +76,7 @@ export type ServiceTokenClaims = {
@@ -76,7 +76,7 @@ export type ServiceTokenClaims = {
*/
*/
iss: string;
iss: string;
sub: string;
sub: string;
- actor_type: "service" | "app" | "mod" | "node" | "agent";
+ actor_type: "service" | "app" | "mod" | "link" | "agent";
/**
/**
* @minItems 1
* @minItems 1
*/
*/
@@ -159209,9 +159209,9 @@ export type AiCredentialPut = {
@@ -159209,9 +159209,9 @@ export type AiCredentialPut = {
/**
/**
* openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost).
* openai = any OpenAI-compatible endpoint. node = the person's own machine: a model on their own model server, through their OpenVibe Link and OpenVibe.Bot (no key, no base_url, no cost).
*/
*/
- provider: "openai" | "anthropic" | "node";
+ provider: "openai" | "anthropic" | "link";
/**
/**
- * provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine.
+ * provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model (the ai plugin, contracts 0.219.0), or `any` (contracts 0.221.0): whichever of their machines is on, lets their AI use its models and offers the model, the fastest first, the next one when it is busy or away. Never anyone else's machine.
*/
*/
machine?: string;
machine?: string;
base_url?: string;
base_url?: string;
@@ -159245,7 +159245,7 @@ export type AiCredentialPutV2 = {
@@ -159245,7 +159245,7 @@ export type AiCredentialPutV2 = {
/**
/**
* anthropic = Anthropic's Messages API; openai = any OpenAI-compatible endpoint; deepseek, minimax and openrouter = those providers; custom = the endpoint in base_url (a URL that speaks the OpenAI or the Anthropic format, by the route it is used on); node = the person's own machine (no key, no base_url, no cost).
* anthropic = Anthropic's Messages API; openai = any OpenAI-compatible endpoint; deepseek, minimax and openrouter = those providers; custom = the endpoint in base_url (a URL that speaks the OpenAI or the Anthropic format, by the route it is used on); node = the person's own machine (no key, no base_url, no cost).
*/
*/
- provider: "anthropic" | "openai" | "deepseek" | "minimax" | "openrouter" | "custom" | "node";
+ provider: "anthropic" | "openai" | "deepseek" | "minimax" | "openrouter" | "custom" | "link";
/**
/**
* The provider key (ai.credential-put@2's name for v1's api_key). Required the first time, and whenever provider or base_url changes (a key is never sent to a new endpoint without being entered again); otherwise the stored key is kept, and it is never returned.
* The provider key (ai.credential-put@2's name for v1's api_key). Required the first time, and whenever provider or base_url changes (a key is never sent to a new endpoint without being entered again); otherwise the stored key is kept, and it is never returned.
*/
*/
@@ -159259,7 +159259,7 @@ export type AiCredentialPutV2 = {
@@ -159259,7 +159259,7 @@ export type AiCredentialPutV2 = {
*/
*/
base_url?: string;
base_url?: string;
/**
/**
- * provider node: the person's own machine (openvibe.bot/machines) whose OpenVibe Link runs the model, or `any` of their own.
+ * provider link: the person's own machine (openvibe.link/machines) whose OpenVibe Link runs the model, or `any` of their own.
*/
*/
machine?: string;
machine?: string;
/**
/**
@@ -159290,9 +159290,9 @@ export type AiCredential = {
@@ -159290,9 +159290,9 @@ export type AiCredential = {
* The service that stored it; only it may use or change it.
* The service that stored it; only it may use or change it.
*/
*/
owner: string;
owner: string;
- provider: "openai" | "anthropic" | "node";
+ provider: "openai" | "anthropic" | "link";
/**
/**
- * provider node: the machine it runs on, or `any` of the person's own (contracts 0.221.0)
+ * provider link: the machine it runs on, or `any` of the person's own (contracts 0.221.0)
*/
*/
machine?: string | null;
machine?: string | null;
base_url?: string | null;
base_url?: string | null;
@@ -159326,9 +159326,9 @@ export type AiCredentialV2 = {
@@ -159326,9 +159326,9 @@ export type AiCredentialV2 = {
* The service that stored it; only it may use or change it.
* The service that stored it; only it may use or change it.
*/
*/
owner: string;
owner: string;
- provider: "anthropic" | "openai" | "deepseek" | "minimax" | "openrouter" | "custom" | "node";
+ provider: "anthropic" | "openai" | "deepseek" | "minimax" | "openrouter" | "custom" | "link";
/**
/**
- * provider node: the machine it runs on, or `any` of the person's own
+ * provider link: the machine it runs on, or `any` of the person's own
*/
*/
machine?: string | null;
machine?: string | null;
base_url?: string | null;
base_url?: string | null;
@@ -160444,7 +160444,7 @@ export interface AiInferenceDeployment {
@@ -160444,7 +160444,7 @@ export interface AiInferenceDeployment {
*/
*/
trust_tier?: "first_party" | "owner_node" | "approved_wholesale" | "unverified";
trust_tier?: "first_party" | "owner_node" | "approved_wholesale" | "unverified";
/**
/**
- * Operator slug or `owner:<subject_id>` for a private node.
+ * Operator slug or `owner:<subject_id>` for a private machine.
*/
*/
operator?: string;
operator?: string;
/**
/**
@@ -197303,7 +197303,7 @@ export interface TradeWatchlist {
@@ -197303,7 +197303,7 @@ export interface TradeWatchlist {
/** [email protected] (owner: watch) */
/** [email protected] (owner: watch) */
/**
/**
- * watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Node probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.
+ * watch.watch@1: a user-defined persistent observation (plan T18). It names a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Link probe, or an expensive Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes, its budget and its retention. Carrier preference is binding: an event or webhook source is used when one exists, then ETag/Last-Modified, then a feed, then an API, and browser polling through Run only when none works. A price or stock is 'as of' the observation, null when it stated none.
*/
*/
export interface Watch {
export interface Watch {
id: string;
id: string;
@@ -210287,11 +210287,11 @@ export interface HostSite {
@@ -210287,11 +210287,11 @@ export interface HostSite {
updated_at: string;
updated_at: string;
}
}
-/** [email protected] (owner: network) */
+/** [email protected] (owner: network) */
/**
/**
- * network.node@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.
+ * network.link@1: one machine of the OpenVibe platform as the node registry lists it (ADR-034 section 12; roadmap WS-X1). Roles are what it serves; location is region-level; the beacon is an HTTPS URL a client times to measure its own round trip. No addresses, capacity or secrets are public: a node is found by role and location, and reached through the service that uses it.
*/
*/
-export interface NetworkNode {
+export interface NetworkLink {
id: string;
id: string;
name?: string;
name?: string;
/**
/**
@@ -210351,11 +210351,11 @@ export interface NetworkNode {
@@ -210351,11 +210351,11 @@ export interface NetworkNode {
cell?: string;
cell?: string;
}
}
-/** [email protected] (owner: network) */
+/** [email protected] (owner: network) */
/**
/**
- * network.node-report-request@1: POST /internal/nodes/report on OpenVibe.Network (capability network.node.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.
+ * network.link-report-request@1: POST /internal/links/report on OpenVibe.Network (capability network.link.report, held by the Host principal). The complete set of nodes one Host inventory knows, with their health now: a node absent from a later report of the same source is marked down, never deleted silently.
*/
*/
-export interface NetworkNodeReportRequest {
+export interface NetworkLinkReportRequest {
/**
/**
* The reporting inventory (e.g. the host id).
* The reporting inventory (e.g. the host id).
*/
*/
@@ -210363,15 +210363,15 @@ export interface NetworkNodeReportRequest {
@@ -210363,15 +210363,15 @@ export interface NetworkNodeReportRequest {
/**
/**
* @maxItems 500
* @maxItems 500
*/
*/
- nodes: NetworkNode[];
+ nodes: NetworkLink[];
}
}
-/** [email protected] (owner: network) */
+/** [email protected] (owner: network) */
/**
/**
- * network.node-list-result@1: GET /api/v1/nodes on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().
+ * network.link-list-result@1: GET /api/v1/links on OpenVibe.Network (public, cacheable): every node of the platform, filterable by ?role= and ?region=. The input of openvibe-sdk/geo's nearest().
*/
*/
-export interface NetworkNodeListResult {
- nodes: NetworkNode[];
+export interface NetworkLinkListResult {
+ nodes: NetworkLink[];
generated_at: string;
generated_at: string;
}
}
@@ -210385,7 +210385,7 @@ export interface NetworkMachine {
@@ -210385,7 +210385,7 @@ export interface NetworkMachine {
*/
*/
id: string;
id: string;
/**
/**
- * The machine's node principal on Network (nod_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for.
+ * The machine's node principal on Network (lnk_…): the credential it holds. Absent for a machine Bot knows but Network has no principal for.
*/
*/
principal_id?: string;
principal_id?: string;
/**
/**
@@ -210539,7 +210539,7 @@ export interface NetworkMachineRolesPut {
@@ -210539,7 +210539,7 @@ export interface NetworkMachineRolesPut {
/** [email protected] (owner: network) */
/** [email protected] (owner: network) */
/**
/**
- * network.machine-pairing@1: a one-time code that pairs a machine with the person's account (POST /api/v1/machines/pairings, capability machines.manage). The code is shown once, expires, and works once: the machine redeems it and becomes the person's. The command is what the owner runs on the machine (openvibe.bot/install). The code is never stored in the clear and never returned again.
+ * network.machine-pairing@1: a one-time code that pairs a machine with the person's account (POST /api/v1/machines/pairings, capability machines.manage). The code is shown once, expires, and works once: the machine redeems it and becomes the person's. The command is what the owner runs on the machine (openvibe.link/install). The code is never stored in the clear and never returned again.
*/
*/
export interface NetworkMachinePairing {
export interface NetworkMachinePairing {
/**
/**
@@ -210555,7 +210555,7 @@ export interface NetworkMachinePairing {
@@ -210555,7 +210555,7 @@ export interface NetworkMachinePairing {
*/
*/
expires_at: string;
expires_at: string;
/**
/**
- * The command the owner runs on the machine (openvibe.bot/install, with the network, the pairing id and the code).
+ * The command the owner runs on the machine (openvibe.link/install, with the network, the pairing id and the code).
*/
*/
installer: string;
installer: string;
}
}
@@ -210596,7 +210596,7 @@ export interface NetworkMachineOfflinePayload {
@@ -210596,7 +210596,7 @@ export interface NetworkMachineOfflinePayload {
/** [email protected] (owner: network) */
/** [email protected] (owner: network) */
/**
/**
- * network.machine.paired v1 (OpenVibe.Network, plan L34: the machines hub). A machine was paired with a person's account: it redeemed the one-time code and became theirs. It holds a credential from then on; the code is spent. The machine's device id in Bot is absent until the machine checks in for the first time. Envelope: subject { type: machine, id: <nod_…> }, visibility internal, actor service:network.
+ * network.machine.paired v1 (OpenVibe.Network, plan L34: the machines hub). A machine was paired with a person's account: it redeemed the one-time code and became theirs. It holds a credential from then on; the code is spent. The machine's device id in Bot is absent until the machine checks in for the first time. Envelope: subject { type: machine, id: <lnk_…> }, visibility internal, actor service:network.
*/
*/
export interface NetworkMachinePairedPayload {
export interface NetworkMachinePairedPayload {
/**
/**
@@ -210728,7 +210728,7 @@ export interface NetworkCoinsReadResult {
@@ -210728,7 +210728,7 @@ export interface NetworkCoinsReadResult {
/** [email protected] (owner: network) */
/** [email protected] (owner: network) */
/**
/**
- * What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.node@1) deliberately leaves out.
+ * What one node or external provider can run right now (roadmap WS-Z9, decision 43): capabilities, multidimensional capacity, measured latencies, health and pricing. First-party only: it carries capacity the public node registry (network.link@1) deliberately leaves out.
*/
*/
export type ResourceOffer = {
export type ResourceOffer = {
[k: string]: unknown | undefined;
[k: string]: unknown | undefined;
@@ -210736,7 +210736,7 @@ export type ResourceOffer = {
@@ -210736,7 +210736,7 @@ export type ResourceOffer = {
offer_id: string;
offer_id: string;
kind: "node" | "provider" | "storage" | "delivery" | "runtime" | "agent" | "harness";
kind: "node" | "provider" | "storage" | "delivery" | "runtime" | "agent" | "harness";
/**
/**
- * network.node@1 id, for kind node
+ * network.link@1 id, for kind node
*/
*/
node_id?: string;
node_id?: string;
/**
/**
@@ -210758,7 +210758,7 @@ export type ResourceOffer = {
@@ -210758,7 +210758,7 @@ export type ResourceOffer = {
*/
*/
owner?: string;
owner?: string;
/**
/**
- * 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 Link'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. Beside them, a kind harness names `runtime:<class>` for each platform.runtime-class@1 class it can run (runtime:function, runtime:code, runtime:browser, runtime:linux, runtime:desktop, runtime:gpu); these are ordinary capability names, not reserved, and OpenVibe.Codes already advertises the classes it offers.
+ * 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 Link's job worker, one per class: worker:function, worker:code, worker:browser, worker:linux, worker:desktop and worker:gpu ($defs.reservedWorkerCapabilities). A Link advertises the name of each class it runs (Link's status.capabilities.worker); every other `worker:` name (worker:ffmpeg, worker:ai-gpu) is an ordinary capability name. Beside them, a kind harness names `runtime:<class>` for each platform.runtime-class@1 class it can run (runtime:function, runtime:code, runtime:browser, runtime:linux, runtime:desktop, runtime:gpu); these are ordinary capability names, not reserved, and OpenVibe.Codes already advertises the classes it offers.
*/
*/
capabilities: string[];
capabilities: string[];
capacity?: {
capacity?: {
@@ -211209,11 +211209,48 @@ export interface TelemetrySample {
@@ -211209,11 +211209,48 @@ export interface TelemetrySample {
};
};
}
}
-/** [email protected] (owner: network) */
+/** [email protected] (owner: network) */
/**
/**
- * Node hardware, placement regions, tags, and operating costs (T1 Universal Fabric; ADR-034 proposed).
+ * Where a person's work can run. Only ready candidates may be picked; a missing rental quote is never a price.
*/
*/
-export interface NodeCapabilities {
+export interface LinkCompute {
+ sources: {
+ id?: string;
+ principal_id?: string;
+ kind: "personal" | "cloud" | "exchange" | "community";
+ name: string;
+ status: "ready" | "unavailable" | "coming_soon";
+ reason: string | null;
+ price?: {
+ amount_usd: number;
+ unit: "hour";
+ } | null;
+ quoted_at?: string;
+ expires_at?: string;
+ }[];
+ candidates: ({
+ id?: string;
+ principal_id?: string;
+ kind: "personal" | "cloud" | "exchange" | "community";
+ name: string;
+ status: "ready" | "unavailable" | "coming_soon";
+ reason: string | null;
+ price?: {
+ amount_usd: number;
+ unit: "hour";
+ } | null;
+ quoted_at?: string;
+ expires_at?: string;
+ } & {
+ id: string;
+ })[];
+}
+
+/** [email protected] (owner: network) */
+/**
+ * Link machine hardware, placement regions, tags, and operating costs (T1 Universal Fabric; ADR-034 proposed).
+ */
+export interface LinkCapabilities {
node_id: string;
node_id: string;
cpu: {
cpu: {
cores: number;
cores: number;
@@ -262643,7 +262680,7 @@ export type BotJobDispatchResult =
@@ -262643,7 +262680,7 @@ export type BotJobDispatchResult =
*/
*/
subject: string | null;
subject: string | null;
/**
/**
- * The offer's provider, as Run sent it; null for a first-party node.
+ * The offer's provider, as Run sent it; null for a first-party machine.
*/
*/
provider: string | null;
provider: string | null;
/**
/**
@@ -262733,7 +262770,7 @@ export type BotJobDispatchResult =
@@ -262733,7 +262770,7 @@ export type BotJobDispatchResult =
*/
*/
subject: string | null;
subject: string | null;
/**
/**
- * The offer's provider, as Run sent it; null for a first-party node.
+ * The offer's provider, as Run sent it; null for a first-party machine.
*/
*/
provider: string | null;
provider: string | null;
/**
/**
@@ -263337,7 +263374,7 @@ export type RunJobReadResult = {
@@ -263337,7 +263374,7 @@ export type RunJobReadResult = {
*/
*/
node: string;
node: string;
/**
/**
- * The offer's provider (the usage reading's `provider`); null for a first-party node.
+ * The offer's provider (the usage reading's `provider`); null for a first-party machine.
*/
*/
provider: string | null;
provider: string | null;
region: string | null;
region: string | null;
@@ -263557,7 +263594,7 @@ export interface RunJobStartedPayload {
@@ -263557,7 +263594,7 @@ export interface RunJobStartedPayload {
*/
*/
node: string;
node: string;
/**
/**
- * The offer's provider (the usage reading's `provider`); null for a first-party node.
+ * The offer's provider (the usage reading's `provider`); null for a first-party machine.
*/
*/
provider: string | null;
provider: string | null;
region: string | null;
region: string | null;
@@ -263617,7 +263654,7 @@ export interface RunJobSucceededPayload {
@@ -263617,7 +263654,7 @@ export interface RunJobSucceededPayload {
*/
*/
node: string;
node: string;
/**
/**
- * The offer's provider (the usage reading's `provider`); null for a first-party node.
+ * The offer's provider (the usage reading's `provider`); null for a first-party machine.
*/
*/
provider: string | null;
provider: string | null;
region: string | null;
region: string | null;
@@ -263689,7 +263726,7 @@ export interface RunJobFailedPayload {
@@ -263689,7 +263726,7 @@ export interface RunJobFailedPayload {
*/
*/
node: string;
node: string;
/**
/**
- * The offer's provider (the usage reading's `provider`); null for a first-party node.
+ * The offer's provider (the usage reading's `provider`); null for a first-party machine.
*/
*/
provider: string | null;
provider: string | null;
region: string | null;
region: string | null;
@@ -263764,7 +263801,7 @@ export interface RunJobCancelledPayload {
@@ -263764,7 +263801,7 @@ export interface RunJobCancelledPayload {
*/
*/
node: string;
node: string;
/**
/**
- * The offer's provider (the usage reading's `provider`); null for a first-party node.
+ * The offer's provider (the usage reading's `provider`); null for a first-party machine.
*/
*/
provider: string | null;
provider: string | null;
region: string | null;
region: string | null;
@@ -263833,7 +263870,7 @@ export interface RunJobExpiredPayload {
@@ -263833,7 +263870,7 @@ export interface RunJobExpiredPayload {
*/
*/
node: string;
node: string;
/**
/**
- * The offer's provider (the usage reading's `provider`); null for a first-party node.
+ * The offer's provider (the usage reading's `provider`); null for a first-party machine.
*/
*/
provider: string | null;
provider: string | null;
region: string | null;
region: string | null;
@@ -263988,11 +264025,11 @@ export interface ResourceControlResult {
@@ -263988,11 +264025,11 @@ export interface ResourceControlResult {
at: string;
at: string;
}
}
-/** [email protected] (owner: network) */
+/** [email protected] (owner: network) */
/**
/**
- * network.node.revoked v1 (OpenVibe.Network; plan T2, docs/t2-cells-and-node-principal.md section 10, migrations/0014_node_pairing.sql). A node principal (nod_…), a person's paired machine, was revoked: through POST /internal/node-principals/:id/revoke (network.node.manage, the service that paired it), through POST /api/v1/me/nodes/:id/revoke (the owner's session), or with the owner's account. Network revokes the row and clears the credential and its grace window; a machine holding a live node token is refused at its next use (≤ 300 s), and until this event only learned of the revoke at its next reauth (≤ 330 s). A consumer stops the machine at once; a second revoke changes nothing and emits nothing. The credential and its hash are never carried. Envelope: subject { type: node_principal, id: <nod_…> }, visibility internal, actor the service (svc:<service>) or the owner ({ type: user }) that revoked.
+ * network.link.revoked v1 (OpenVibe.Network; plan T2, docs/t2-cells-and-node-principal.md section 10, migrations/0014_node_pairing.sql). A node principal (lnk_…), a person's paired machine, was revoked: through POST /internal/link-principals/:id/revoke (network.link.manage, the service that paired it), through POST /api/v1/me/nodes/:id/revoke (the owner's session), or with the owner's account. Network revokes the row and clears the credential and its grace window; a machine holding a live node token is refused at its next use (≤ 300 s), and until this event only learned of the revoke at its next reauth (≤ 330 s). A consumer stops the machine at once; a second revoke changes nothing and emits nothing. The credential and its hash are never carried. Envelope: subject { type: node_principal, id: <lnk_…> }, visibility internal, actor the service (svc:<service>) or the owner ({ type: user }) that revoked.
*/
*/
-export interface NetworkNodeRevokedPayload {
+export interface NetworkLinkRevokedPayload {
/**
/**
* The machine's node id (platform_node_principals.node_id), minted with the principal and unique among principals.
* The machine's node id (platform_node_principals.node_id), minted with the principal and unique among principals.
*/
*/
@@ -294275,7 +294312,7 @@ export interface BenchRun {
@@ -294275,7 +294312,7 @@ export interface BenchRun {
};
};
executor: {
executor: {
/**
/**
- * codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it.
+ * codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it.
*/
*/
kind: "codes" | "run" | "node" | "submitted";
kind: "codes" | "run" | "node" | "submitted";
id: string;
id: string;
@@ -294346,7 +294383,7 @@ export interface BenchRunV2 {
@@ -294346,7 +294383,7 @@ export interface BenchRunV2 {
};
};
executor: {
executor: {
/**
/**
- * codes: an openvibe.codes task. run: a Run worker. node: the person's own machine. submitted: nobody ran it.
+ * codes: an openvibe.codes task. run: a Run worker. link: the person's own machine. submitted: nobody ran it.
*/
*/
kind: "codes" | "run" | "node" | "submitted";
kind: "codes" | "run" | "node" | "submitted";
id: string;
id: string;
@@ -294807,7 +294844,7 @@ export interface TodayReminderResult {
@@ -294807,7 +294844,7 @@ export interface TodayReminderResult {
/** [email protected] (owner: connect) */
/** [email protected] (owner: connect) */
/**
/**
- * website.share@1: one Share on OpenVibe.Website Connect, a local port published at a stable hostname through the edge relay. The record says who owns it, which project scopes it, which Node tunnel serves it, which local target it may reach, which policy version the edge enforces and where it is in its life (draft, review, active, paused, revoked). The tunnel token, the request bodies the inspector captures and the policy's password hash are never part of this document.
+ * website.share@1: one Share on OpenVibe.Website Connect, a local port published at a stable hostname through the edge relay. The record says who owns it, which project scopes it, which Link tunnel serves it, which local target it may reach, which policy version the edge enforces and where it is in its life (draft, review, active, paused, revoked). The tunnel token, the request bodies the inspector captures and the policy's password hash are never part of this document.
*/
*/
export interface WebsiteShare {
export interface WebsiteShare {
id: string;
id: string;
@@ -294861,7 +294898,7 @@ export interface WebsiteShare {
@@ -294861,7 +294898,7 @@ export interface WebsiteShare {
*/
*/
expires_at?: string | null;
expires_at?: string | null;
/**
/**
- * The last successful Node liveness check; null until the first tunnel.
+ * The last successful Link liveness check; null until the first tunnel.
*/
*/
last_seen_at?: string | null;
last_seen_at?: string | null;
created_at: string;
created_at: string;
@@ -294896,7 +294933,7 @@ export interface WebsiteShareCreateRequest {
@@ -294896,7 +294933,7 @@ export interface WebsiteShareCreateRequest {
password_set?: never;
password_set?: never;
};
};
/**
/**
- * Optional: bind the share to this Node principal now. Omit to bind it when the first tunnel connects.
+ * Optional: bind the share to this Link principal now. Omit to bind it when the first tunnel connects.
*/
*/
node_id?: string;
node_id?: string;
/**
/**
@@ -303479,7 +303516,7 @@ export interface ChatParty {
@@ -303479,7 +303516,7 @@ export interface ChatParty {
/** [email protected] (owner: bot) */
/** [email protected] (owner: bot) */
/**
/**
- * bot.machine-pool@1 (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC, `rpc-server` on the members and `llama-server --rpc` on the head. OpenVibe.Network holds the record: the owner proposes it on /machines (POST /api/v1/machine-pools, two to eight of their own machines and an optional head; the default head has the most free VRAM), and each machine reads it with its own node token (GET /api/v1/node/self/pools/:id, network.node.self.manage) and approves it locally (POST /api/v1/node/self/pools/:id/approve with { certificate }). A machine's `certificate` is null until it approves on that machine: the Link generates an Ed25519 key there, keeps the private key, and uploads only its self-signed public certificate, which the other members pin for the pool's private link. A pool with any null certificate never serves. The record never carries a private key or a prompt. What a schema cannot say, Network and Node check at run time: the members are distinct and the head is one of them; every member has the pool's owner, today, through Bot; every member was seen from the same public address with a private address in the same subnet; the head's direct link probe to each member passes; each machine's certificate is its own; and any change of membership, owner, head or key revokes the pool, so all machines approve a new one. A machine that leaves (removed, signed out, a different owner) drops its link and its key at once.
+ * bot.machine-pool@1 (the pooled machines design, 2026-11-06): several of one owner's machines on the same local network that serve one model together through llama.cpp RPC, `rpc-server` on the members and `llama-server --rpc` on the head. OpenVibe.Network holds the record: the owner proposes it on /machines (POST /api/v1/machine-pools, two to eight of their own machines and an optional head; the default head has the most free VRAM), and each machine reads it with its own Link token (GET /api/v1/link/self/pools/:id, network.link.self.manage) and approves it locally (POST /api/v1/link/self/pools/:id/approve with { certificate }). A machine's `certificate` is null until it approves on that machine: the Link generates an Ed25519 key there, keeps the private key, and uploads only its self-signed public certificate, which the other members pin for the pool's private link. A pool with any null certificate never serves. The record never carries a private key or a prompt. What a schema cannot say, Network and Link check at run time: the members are distinct and the head is one of them; every member has the pool's owner, today, through Bot; every member was seen from the same public address with a private address in the same subnet; the head's direct link probe to each member passes; each machine's certificate is its own; and any change of membership, owner, head or key revokes the pool, so all machines approve a new one. A machine that leaves (removed, signed out, a different owner) drops its link and its key at once.
*/
*/
export interface BotMachinePool {
export interface BotMachinePool {
/**
/**
@@ -6,7 +6,7 @@
@@ -6,7 +6,7 @@
const crypto = require('crypto');
const crypto = require('crypto');
const ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
const ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
-const PREFIX = { user: 'usr', guest: 'gst', app: 'app', mod: 'mod', agent: 'agt', node: 'nod', event: 'evt', media: 'med', project: 'prj', confirmation: 'cnf', consent: 'cns', job: 'job', task: 'tsk', watch: 'wch', wait: 'wai', observation: 'wco', check: 'ckr', subscription: 'sub', deploy: 'dpl', domain: 'dom', manifest: 'mfs', release: 'rel', site: 'sit', robot: 'rob', stream: 'std', webhook_delivery: 'whd', share: 'shr', device: 'dev', hook: 'hok', policy: 'pol', audit: 'aud', agent_stream: 'ags', agent_stream_schedule: 'scd', play_request: 'req', sandbox_session: 'sbs', party: 'pty' };
+const PREFIX = { user: 'usr', guest: 'gst', app: 'app', mod: 'mod', agent: 'agt', link: 'lnk', event: 'evt', media: 'med', project: 'prj', confirmation: 'cnf', consent: 'cns', job: 'job', task: 'tsk', watch: 'wch', wait: 'wai', observation: 'wco', check: 'ckr', subscription: 'sub', deploy: 'dpl', domain: 'dom', manifest: 'mfs', release: 'rel', site: 'sit', robot: 'rob', stream: 'std', webhook_delivery: 'whd', share: 'shr', device: 'dev', hook: 'hok', policy: 'pol', audit: 'aud', agent_stream: 'ags', agent_stream_schedule: 'scd', play_request: 'req', sandbox_session: 'sbs', party: 'pty' };
const SLUG_TYPES = new Set(['service', 'system']);
const SLUG_TYPES = new Set(['service', 'system']);
const ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
const ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
const SLUG_RE = /^[a-z][a-z0-9-]{1,39}$/;
const SLUG_RE = /^[a-z][a-z0-9-]{1,39}$/;
@@ -50,11 +50,11 @@ function formatSubject(ref) {
@@ -50,11 +50,11 @@ function formatSubject(ref) {
return `${ref.type}:${ref.id}`;
return `${ref.type}:${ref.id}`;
}
}
-/** Service-token `sub` claim: svc:<slug> | app:app_<ULID> | mod:mod_<ULID> | agent:agt_<ULID> | node:nod_<ULID>. */
+/** Service-token `sub` claim: svc:<slug> | app:app_<ULID> | mod:mod_<ULID> | agent:agt_<ULID> | link:lnk_<ULID>. */
function principalSub(ref) {
function principalSub(ref) {
if (ref.type === 'service' && SLUG_RE.test(ref.id)) return `svc:${ref.id}`;
if (ref.type === 'service' && SLUG_RE.test(ref.id)) return `svc:${ref.id}`;
- if ((ref.type === 'app' || ref.type === 'mod' || ref.type === 'agent' || ref.type === 'node') && isSubjectId(ref.type, ref.id)) return `${ref.type}:${ref.id}`;
- throw new TypeError('principals are service, app, mod, agent or node subjects');
+ if ((ref.type === 'app' || ref.type === 'mod' || ref.type === 'agent' || ref.type === 'link') && isSubjectId(ref.type, ref.id)) return `${ref.type}:${ref.id}`;
+ throw new TypeError('principals are service, app, mod, agent or link subjects');
}
}
/** Transitional Media reference for today's rows (Wave 4 introduces med_ ids). */
/** Transitional Media reference for today's rows (Wave 4 introduces med_ ids). */
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "first-party",
"visibility": "first-party",
"sensitive": true,
"sensitive": true,
- "description": "Use a person's own computer with a Network service token naming that person in X-OV-Subject: the computer kinds of bot.command@1 (exec, fs, screen, input, browser) through POST /api/v1/robots/:id/commands, for a machine (openvibe.bot/machines) whose owner is that person and who turned on 'Let my agent use this computer'. Bot takes them from no one else (never an operator or a queue member), and the machine's owner sets on the machine itself what may happen (OpenVibe Link's computer plugin: observe, ask, trusted or full; ask waits for the owner's approval there). The answer is bot.command-result@1's data; every command is audited, a refused one emits bot.command.refused. OpenVibe.Watch (a `node` source) may send only `probe` — one GET or one TCP connect to a private, loopback or link-local address from the machine — and only where the owner turned on \"Let my watches check things from this computer\". The live screen (screen_stream) is not part of it: only the machine's owner starts it, on openvibe.bot, and Bot fills in the machine's private stream.",
+ "description": "Use a person's own computer with a Network service token naming that person in X-OV-Subject: the computer kinds of bot.command@1 (exec, fs, screen, input, browser) through POST /api/v1/robots/:id/commands, for a machine (openvibe.link/machines) whose owner is that person and who turned on 'Let my agent use this computer'. Bot takes them from no one else (never an operator or a queue member), and the machine's owner sets on the machine itself what may happen (OpenVibe Link's computer plugin: observe, ask, trusted or full; ask waits for the owner's approval there). The answer is bot.command-result@1's data; every command is audited, a refused one emits bot.command.refused. OpenVibe.Watch (a `node` source) may send only `probe` — one GET or one TCP connect to a private, loopback or link-local address from the machine — and only where the owner turned on \"Let my watches check things from this computer\". The live screen (screen_stream) is not part of it: only the machine's owner starts it, on openvibe.bot, and Bot fills in the machine's private stream.",
"permissions": [
"permissions": [
"bot:computer:control"
"bot:computer:control"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "active",
"status": "active",
"visibility": "first-party",
"visibility": "first-party",
"sensitive": true,
"sensitive": true,
- "description": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.network/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",
+ "description": "A service acts for a person on their machines through Bot's machines API: it lists, renames, gives roles to and revokes the machines of the person it names (X-OV-Subject). Network holds it for the machines hub (openvibe.link/machines): Bot stays the registry underneath, so Bot checks that the machine is the named person's own. Never grantable to apps.",
"permissions": [
"permissions": [
"bot:machine:read",
"bot:machine:read",
"bot:machine:rename",
"bot:machine:rename",
new file mode 100644
new file mode 100644
@@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
+{
+ "id": "network.link.manage",
+ "version": "1.0",
+ "owner": "network",
+ "status": "active",
+ "visibility": "internal",
+ "description": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",
+ "permissions": [
+ "nodes:pair",
+ "nodes:read",
+ "nodes:revoke"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "admin",
+ "events": ["network.link.revoked"],
+ "implementedBy": [
+ "POST /internal/link-pairings",
+ "GET /internal/link-principals/:id",
+ "POST /internal/link-principals/:id/revoke"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "id": "network.link.report",
+ "version": "1.0",
+ "owner": "network",
+ "status": "active",
+ "visibility": "internal",
+ "description": "Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",
+ "permissions": [
+ "nodes:write"
+ ],
+ "resourceConstraints": [
+ "none"
+ ],
+ "quotaClass": "admin",
+ "events": [],
+ "implementedBy": [
+ "POST /internal/links/report",
+ "POST /internal/registry/instances/report"
+ ],
+ "inputSchema": "network.link-report-request@1",
+ "outputSchema": "network.link-list-result@1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
+{
+ "id": "network.link.self.manage",
+ "version": "1.0",
+ "owner": "network",
+ "status": "active",
+ "visibility": "internal",
+ "description": "A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
+ "permissions": [
+ "nodes:self"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "admin",
+ "events": [],
+ "implementedBy": [
+ "PUT /api/v1/link/self/capabilities",
+ "POST /api/v1/link/self/credential",
+ "POST /api/v1/link/self/pools/announce",
+ "GET /api/v1/link/self/pools/:id",
+ "POST /api/v1/link/self/pools/:id/approve",
+ "POST /api/v1/link/self/pools/:id/status",
+ "POST /api/v1/link/self/pools/:id/leave"
+ ]
+}
deleted file mode 100644
deleted file mode 100644
@@ -1,23 +0,0 @@
@@ -1,23 +0,0 @@
-{
- "id": "network.node.manage",
- "version": "1.0",
- "owner": "network",
- "status": "active",
- "visibility": "internal",
- "description": "A service mints one-time pairing codes for its users' machines and reads or revokes the node principals it paired. Never grantable to apps.",
- "permissions": [
- "nodes:pair",
- "nodes:read",
- "nodes:revoke"
- ],
- "resourceConstraints": [
- "owner"
- ],
- "quotaClass": "admin",
- "events": ["network.node.revoked"],
- "implementedBy": [
- "POST /internal/node-pairings",
- "GET /internal/node-principals/:id",
- "POST /internal/node-principals/:id/revoke"
- ]
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,22 +0,0 @@
@@ -1,22 +0,0 @@
-{
- "id": "network.node.report",
- "version": "1.0",
- "owner": "network",
- "status": "active",
- "visibility": "internal",
- "description": "Report the machines of the platform (roles, region-level location, beacon, health) to the node registry, which the geo API and every location-aware product read (ADR-034 section 12).",
- "permissions": [
- "nodes:write"
- ],
- "resourceConstraints": [
- "none"
- ],
- "quotaClass": "admin",
- "events": [],
- "implementedBy": [
- "POST /internal/nodes/report",
- "POST /internal/registry/instances/report"
- ],
- "inputSchema": "network.node-report-request@1",
- "outputSchema": "network.node-list-result@1"
-}
deleted file mode 100644
deleted file mode 100644
@@ -1,25 +0,0 @@
@@ -1,25 +0,0 @@
-{
- "id": "network.node.self.manage",
- "version": "1.0",
- "owner": "network",
- "status": "active",
- "visibility": "internal",
- "description": "A paired machine presents its own capabilities and rotates its own credential, never another machine's. It also announces itself for its owner's machine pools (bot.machine-pool@1), reads, approves and renews the pools it is a member of, and leaves them; it never acts for another machine.",
- "permissions": [
- "nodes:self"
- ],
- "resourceConstraints": [
- "owner"
- ],
- "quotaClass": "admin",
- "events": [],
- "implementedBy": [
- "PUT /api/v1/node/self/capabilities",
- "POST /api/v1/node/self/credential",
- "POST /api/v1/node/self/pools/announce",
- "GET /api/v1/node/self/pools/:id",
- "POST /api/v1/node/self/pools/:id/approve",
- "POST /api/v1/node/self/pools/:id/status",
- "POST /api/v1/node/self/pools/:id/leave"
- ]
-}
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "planned",
"status": "planned",
"visibility": "first-party",
"visibility": "first-party",
"sensitive": true,
"sensitive": true,
- "description": "PLANNED for OpenVibe.Website Connect (audience openvibe.website, node principal): a Link opens one share's tunnel with POST /api/v1/shares/:id/tunnel and a website.share-connect-request@1. The edge checks the Network node token (client credentials, subject node:nod_…, audience openvibe.website) and the short-lived, single-use tunnel token minted by Connect; neither alone opens the route. The node token's subject must equal node:<node_id> from the request, and a mismatch is refused and audited. Not delegated to user or app tokens.",
+ "description": "PLANNED for OpenVibe.Website Connect (audience openvibe.website, link principal): a Link opens one share's tunnel with POST /api/v1/shares/:id/tunnel and a website.share-connect-request@1. The edge checks the Network Link token (client credentials, subject link:lnk_…, audience openvibe.website) and the short-lived, single-use tunnel token minted by Connect; neither alone opens the route. The Link token's subject must equal link:<node_id> from the request, and a mismatch is refused and audited. Not delegated to user or app tokens.",
"permissions": [
"permissions": [
"website:tunnels:connect"
"website:tunnels:connect"
],
],
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"status": "planned",
"status": "planned",
"visibility": "public",
"visibility": "public",
"sensitive": true,
"sensitive": true,
- "description": "PLANNED for OpenVibe.Website Connect (audience openvibe.website, owner or project scope): revoke and delete a share with DELETE /api/v1/shares/:id. Connect writes website.share.revoked, closes the edge route and tunnel within its 5-second revocation budget and keeps the audit trail; a public hostname returns 410, not a cached page. Deleting a node in Network closes every share for that node through network.node.revoked.",
+ "description": "PLANNED for OpenVibe.Website Connect (audience openvibe.website, owner or project scope): revoke and delete a share with DELETE /api/v1/shares/:id. Connect writes website.share.revoked, closes the edge route and tunnel within its 5-second revocation budget and keeps the audit trail; a public hostname returns 410, not a cached page. Deleting a node in Network closes every share for that node through network.link.revoked.",
"permissions": [
"permissions": [
"website:shares:delete"
"website:shares:delete"
],
],
@@ -47,7 +47,7 @@
@@ -47,7 +47,7 @@
},
},
{
{
"question": "Can an agent take full control of my computer?",
"question": "Can an agent take full control of my computer?",
- "answer": "Only if you set that Node to Full control on the device itself. It is time-boxed and recorded, you can see when an agent is in control, and a button, a hotkey or one command ends it. Most people use Trusted: routine actions run and risky ones ask first."
+ "answer": "Only if you set that Link to Full control on the device itself. It is time-boxed and recorded, you can see when an agent is in control, and a button, a hotkey or one command ends it. Most people use Trusted: routine actions run and risky ones ask first."
},
},
{
{
"question": "Can I run Actor myself?",
"question": "Can I run Actor myself?",
@@ -234,7 +234,7 @@
@@ -234,7 +234,7 @@
[
[
"fa-robot",
"fa-robot",
"Robots too",
"Robots too",
- "The same Node drives robots on OpenVibe.Bot, so one install covers your computers and your robots.",
+ "The same Link drives robots on OpenVibe.Bot, so one install covers your computers and your robots.",
"https://openvibe.bot"
"https://openvibe.bot"
]
]
]
]
@@ -257,7 +257,7 @@
@@ -257,7 +257,7 @@
[
[
"fa-server",
"fa-server",
"Devices",
"Devices",
- "Every Node with its status, a terminal, its files and its screen."
+ "Every Link with its status, a terminal, its files and its screen."
],
],
[
[
"fa-robot",
"fa-robot",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
- "name": "OpenVibe.Node",
- "repository": "OpenVibers/OpenVibe.Node",
+ "name": "OpenVibe.Link",
+ "repository": "OpenVibers/OpenVibe.Link",
"service": null,
"service": null,
"product": null,
"product": null,
"stage": "alpha",
"stage": "alpha",
@@ -32,7 +32,7 @@
@@ -32,7 +32,7 @@
},
},
{
{
"question": "Can it run on my own machine?",
"question": "Can it run on my own machine?",
- "answer": "Yes. With OpenVibe.Run and the OpenVibe node on your computer, a task can run on your own hardware instead of a server."
+ "answer": "Yes. With OpenVibe.Run and the OpenVibe Link on your computer, a task can run on your own hardware instead of a server."
},
},
{
{
"question": "Where is the code?",
"question": "Where is the code?",
@@ -69,9 +69,9 @@
@@ -69,9 +69,9 @@
"network.integration.github.read",
"network.integration.github.read",
"network.modules.read",
"network.modules.read",
"network.modules.write",
"network.modules.write",
- "network.node.manage",
- "network.node.report",
- "network.node.self.manage",
+ "network.link.manage",
+ "network.link.report",
+ "network.link.self.manage",
"network.notifications.push",
"network.notifications.push",
"network.operator.alert",
"network.operator.alert",
"network.project.manage",
"network.project.manage",
@@ -105,7 +105,7 @@
@@ -105,7 +105,7 @@
"network.grant.changed",
"network.grant.changed",
"network.mod.grants_changed",
"network.mod.grants_changed",
"network.module.updated",
"network.module.updated",
- "network.node.revoked",
+ "network.link.revoked",
"network.notification.created",
"network.notification.created",
"network.principal_grant.changed",
"network.principal_grant.changed",
"network.subject.merged",
"network.subject.merged",
@@ -95,7 +95,7 @@
@@ -95,7 +95,7 @@
"none": "no claims: one process runs the scheduler; check.run() refuses a second check of a watch in that process"
"none": "no claims: one process runs the scheduler; check.run() refuses a second check of a watch in that process"
}
}
},
},
- "notes": "Port 4730, unit openvibe-watch.service. Persistent, user-defined observations and conditions over pages, feeds and APIs (plan T18): the registry, the check engine with the pull carriers (http, feed, api; conditional GET, per-host spacing, backoff, Retry-After), observations and watch.* events through a transactional outbox, the bearer API (/api/v1, loopback) and, since 0.2.0, the public site at openvibe.watch: sign in with OpenVibe (OAuth client watch, PKCE S256, sessions in Watch's database) and create, edit, pause, check and delete your watches in the browser. Event, webhook, Run and Node carriers, AI extraction and actions come in later T18 steps and are not offered. A fired condition becomes a WATCH_TRIGGERED notification through OpenVibe.Network.",
+ "notes": "Port 4730, unit openvibe-watch.service. Persistent, user-defined observations and conditions over pages, feeds and APIs (plan T18): the registry, the check engine with the pull carriers (http, feed, api; conditional GET, per-host spacing, backoff, Retry-After), observations and watch.* events through a transactional outbox, the bearer API (/api/v1, loopback) and, since 0.2.0, the public site at openvibe.watch: sign in with OpenVibe (OAuth client watch, PKCE S256, sessions in Watch's database) and create, edit, pause, check and delete your watches in the browser. Event, webhook, Run and Link carriers, AI extraction and actions come in later T18 steps and are not offered. A fired condition becomes a WATCH_TRIGGERED notification through OpenVibe.Network.",
"health": "/api/health",
"health": "/api/health",
"ready": "/api/ready",
"ready": "/api/ready",
"internalOrigin": "http://127.0.0.1:4730",
"internalOrigin": "http://127.0.0.1:4730",
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.371.0",
+ "version": "0.372.0",
"lockfileVersion": 3,
"lockfileVersion": 3,
"requires": true,
"requires": true,
"packages": {
"packages": {
"": {
"": {
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.371.0",
+ "version": "0.372.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.371.0",
+ "version": "0.372.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",
@@ -23,7 +23,7 @@
@@ -23,7 +23,7 @@
* touched package.json's pins (so a bump without a STATUS refresh is caught): checked by the date only.
* touched package.json's pins (so a bump without a STATUS refresh is caught): checked by the date only.
*
*
* Every other checkout under the root named OpenVibe.* (a client or repository with no service manifest, such as
* Every other checkout under the root named OpenVibe.* (a client or repository with no service manifest, such as
- * OpenVibe.Node or OpenVibe.Extensions) needs a STATUS.json with repository, stage, deployed, features and updated, so
+ * OpenVibe.Link or OpenVibe.Extensions) needs a STATUS.json with repository, stage, deployed, features and updated, so
* the estate does not show it blank; its README is not held to the service headings. One whose STATUS `stage` is
* the estate does not show it blank; its README is not held to the service headings. One whose STATUS `stage` is
* closed, retired or archived (OpenVibe.Realtime, closed by ADR-005) is skipped.
* closed, retired or archived (OpenVibe.Realtime, closed by ADR-005) is skipped.
*
*
@@ -438,7 +438,7 @@ function checkRepo(root, manifest, ctx) {
@@ -438,7 +438,7 @@ function checkRepo(root, manifest, ctx) {
}
}
/**
/**
- * A checkout with no service manifest (OpenVibe.Node, OpenVibe.Extensions): it needs a STATUS.json the estate can
+ * A checkout with no service manifest (OpenVibe.Link, OpenVibe.Extensions): it needs a STATUS.json the estate can
* read, and its facts are checked; the service README headings are not asked of it.
* read, and its facts are checked; the service README headings are not asked of it.
*/
*/
function checkCheckout(root, name, ctx) {
function checkCheckout(root, name, ctx) {
@@ -15,7 +15,7 @@
@@ -15,7 +15,7 @@
* package.json lacks is read from a nested one (apps/server, examples/<name>: at most three levels
* package.json lacks is read from a nested one (apps/server, examples/<name>: at most three levels
* down, never node_modules), then from STATUS.json's prose
* down, never node_modules), then from STATUS.json's prose
* STATUS.json repository, stage, runtime, domain, database, deployed and wave; version when there is no
* STATUS.json repository, stage, runtime, domain, database, deployed and wave; version when there is no
- * package.json (a Go repository such as OpenVibe.Node)
+ * package.json (a Go repository such as OpenVibe.Link)
* migrations/ the count and the dialect the SQL declares
* migrations/ the count and the dialect the SQL declares
* this repository's manifests/services|products/*.json the service or product the checkout is
* this repository's manifests/services|products/*.json the service or product the checkout is
*
*
@@ -188,20 +188,20 @@ try {
@@ -188,20 +188,20 @@ try {
// A checkout with no service manifest: OpenVibe.* needs a STATUS.json; closed ones and other names are skipped.
// A checkout with no service manifest: OpenVibe.* needs a STATUS.json; closed ones and other names are skipped.
const manifests = [service('lib', 'OpenVibe.Lib')];
const manifests = [service('lib', 'OpenVibe.Lib')];
- checkout('OpenVibe.Node', { 'README.md': '# Node\n', 'go.mod': 'module x\n' });
+ checkout('OpenVibe.Link', { 'README.md': '# Link\n', 'go.mod': 'module x\n' });
checkout('OpenVibe.Realtime', { 'STATUS.json': { repository: 'OpenVibers/OpenVibe.Realtime', stage: 'closed' } });
checkout('OpenVibe.Realtime', { 'STATUS.json': { repository: 'OpenVibers/OpenVibe.Realtime', stage: 'closed' } });
checkout('AFResume', { 'README.md': '# CV\n' });
checkout('AFResume', { 'README.md': '# CV\n' });
let all = collect(facts, manifests);
let all = collect(facts, manifests);
const byId = (id) => all.find((x) => x.id === id);
const byId = (id) => all.find((x) => x.id === id);
- ok(byId('node') && /STATUS\.json missing/.test(gapsOf(byId('node'))), 'an OpenVibe.* checkout without a STATUS.json is a gap');
+ ok(byId('link') && /STATUS\.json missing/.test(gapsOf(byId('link'))), `an OpenVibe.* checkout without a STATUS.json is a gap: ${JSON.stringify(byId('link'))}`);
ok(byId('realtime') && byId('realtime').skipped === 'closed', 'a closed repository is skipped');
ok(byId('realtime') && byId('realtime').skipped === 'closed', 'a closed repository is skipped');
ok(!all.some((x) => /afresume/i.test(x.id)), 'a checkout not named OpenVibe.* is not the platform\'s');
ok(!all.some((x) => /afresume/i.test(x.id)), 'a checkout not named OpenVibe.* is not the platform\'s');
ok(byId('wiki') && byId('wiki').gaps.some((g) => /STATUS\.json missing/.test(g)), 'every unmanifested OpenVibe.* checkout is held to a STATUS.json');
ok(byId('wiki') && byId('wiki').gaps.some((g) => /STATUS\.json missing/.test(g)), 'every unmanifested OpenVibe.* checkout is held to a STATUS.json');
- checkout('OpenVibe.Node', { 'README.md': '# Node\n', 'STATUS.json': { repository: 'OpenVibers/OpenVibe.Node', stage: 'alpha', version: '0.13.0', deployed: false, features: ['pairing'], updated: '2099-01-01' } });
- all = collect(facts, manifests, new Set(['node']));
- ok(all.length === 1 && all[0].id === 'node' && all[0].gaps.length === 0, `--only reaches a repository checkout, and a complete STATUS is current: ${JSON.stringify(all)}`);
- checkout('OpenVibe.Node', { 'README.md': '# Node\n', 'STATUS.json': { repository: 'OpenVibers/OpenVibe.Node', stage: 'alpha' } });
- all = collect(facts, manifests, new Set(['node']));
+ checkout('OpenVibe.Link', { 'README.md': '# Link\n', 'STATUS.json': { repository: 'OpenVibers/OpenVibe.Link', stage: 'alpha', version: '0.13.0', deployed: false, features: ['pairing'], updated: '2099-01-01' } });
+ all = collect(facts, manifests, new Set(['link']));
+ ok(all.length === 1 && all[0].id === 'link' && all[0].gaps.length === 0, `--only reaches a repository checkout, and a complete STATUS is current: ${JSON.stringify(all)}`);
+ checkout('OpenVibe.Link', { 'README.md': '# Link\n', 'STATUS.json': { repository: 'OpenVibers/OpenVibe.Link', stage: 'alpha' } });
+ all = collect(facts, manifests, new Set(['link']));
ok(all[0].gaps.some((g) => /no "features"/.test(g)) && all[0].gaps.some((g) => /no "deployed"/.test(g)), 'a repository STATUS needs deployed and features');
ok(all[0].gaps.some((g) => /no "features"/.test(g)) && all[0].gaps.some((g) => /no "deployed"/.test(g)), 'a repository STATUS needs deployed and features');
ok(summarize([{ gaps: [] }, { skipped: 'closed', gaps: [] }]) === '1/1 current, 1 closed skipped', 'the summary counts closed repositories apart');
ok(summarize([{ gaps: [] }, { skipped: 'closed', gaps: [] }]) === '1/1 current, 1 closed skipped', 'the summary counts closed repositories apart');
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+'use strict';
+const assert = require('node:assert/strict');
+const { spawnSync } = require('node:child_process');
+const path = require('node:path');
+const root = path.resolve(__dirname, '..');
+const oldName = ['OpenVibe[ .-]No' + 'de', 'openvibe-' + 'node', 'no' + 'd_', 'node[.]openvibe[.]network', 'node:lnk_', 'openvibe[.](bot|my|network)/mach' + 'ines'].join('|');
+const result = spawnSync('git', ['grep', '-n', '-I', '-i', '-E', oldName, '--', '.', ':!migrations/**', ':!test/link-brand.test.js'], { cwd: root, encoding: 'utf8' });
+assert.ok(result.status === 0 || result.status === 1, result.stderr || 'git grep failed');
+assert.equal(result.status, 1, `Old Link brand or principal returned:\n${(result.stdout || '').split('\n').slice(0, 10).join('\n')}`);
+console.log(' ok old Link brand and principal IDs are absent');
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+'use strict';
+const assert = require('node:assert/strict');
+const contracts = require('..');
+const example = require('../contracts/link/compute.v1.json').examples[0];
+
+assert.equal(contracts.validate('link.compute@1', example).valid, true);
+assert.equal(contracts.validate('link.compute@1', {
+ ...example, sources: [...example.sources, { kind: 'exchange', name: 'GPU', status: 'ready', reason: null, price: null }],
+}).valid, false, 'a ready rental needs a live price and quote lifetime');
+assert.equal(contracts.validate('link.compute@1', {
+ ...example, candidates: [{ ...example.candidates[0], price: { amount_usd: -1, unit: 'hour' } }],
+}).valid, false, 'negative prices cannot be displayed');
+assert.equal(contracts.validate('link.compute@1', {
+ ...example, candidates: [{ ...example.candidates[0], status: 'imaginary' }],
+}).valid, false, 'unknown placement state is rejected');
+console.log('link.compute contract: ok');
@@ -3,7 +3,7 @@ const assert = require('assert');
@@ -3,7 +3,7 @@ const assert = require('assert');
const contracts = require('..');
const contracts = require('..');
const names = [
const names = [
- 'usage-sample', 'telemetry-sample', 'node-capabilities', 'service-instance',
+ 'usage-sample', 'telemetry-sample', 'link-capabilities', 'service-instance',
'runtime-offer', 'storage-offer', 'delivery-offer', 'agent-offer', 'harness-offer', 'job',
'runtime-offer', 'storage-offer', 'delivery-offer', 'agent-offer', 'harness-offer', 'job',
];
];
@@ -38,7 +38,7 @@ function reading(id, startedMs, n, quantity) {
@@ -38,7 +38,7 @@ function reading(id, startedMs, n, quantity) {
return {
return {
id: key, idempotency_key: key, service: 'run', project: record.project, subject: record.subject, resource: id,
id: key, idempotency_key: key, service: 'run', project: record.project, subject: record.subject, resource: id,
node: record.node, operation: 'function.invoke', quantity, unit: 's', at: new Date(startedMs + n * 1000).toISOString(),
node: record.node, operation: 'function.invoke', quantity, unit: 's', at: new Date(startedMs + n * 1000).toISOString(),
- source: 'openvibe-node.worker',
+ source: 'openvibe-link.worker',
};
};
}
}
const fromUsage = (f) => reading(f.id, f.started_ms, f.second, 1);
const fromUsage = (f) => reading(f.id, f.started_ms, f.second, 1);
@@ -36,6 +36,9 @@ function sub(file, ...args) {
@@ -36,6 +36,9 @@ function sub(file, ...args) {
for (const line of skipsIn(`${r.stdout}${r.stderr}`)) if (!skips.includes(line)) skips.push(line);
for (const line of skipsIn(`${r.stdout}${r.stderr}`)) if (!skips.includes(line)) skips.push(line);
}
}
+sub(path.join(ROOT, 'test/link-brand.test.js'));
+sub(path.join(ROOT, 'test/link-compute.test.js'));
+
// ── Catalog integrity ────────────────────────────────────────────────────
// ── Catalog integrity ────────────────────────────────────────────────────
const files = [];
const files = [];
(function walk(d) { for (const e of fs.readdirSync(d, { withFileTypes: true })) { const p = path.join(d, e.name); if (e.isDirectory()) walk(p); else if (/\.v\d+\.json$/.test(e.name)) files.push(path.relative(path.join(ROOT, 'contracts'), p)); } })(path.join(ROOT, 'contracts'));
(function walk(d) { for (const e of fs.readdirSync(d, { withFileTypes: true })) { const p = path.join(d, e.name); if (e.isDirectory()) walk(p); else if (/\.v\d+\.json$/.test(e.name)) files.push(path.relative(path.join(ROOT, 'contracts'), p)); } })(path.join(ROOT, 'contracts'));
@@ -610,10 +613,10 @@ for (const kind of ['user', 'guest', 'app', 'mod']) {
@@ -610,10 +613,10 @@ for (const kind of ['user', 'guest', 'app', 'mod']) {
ok(ids.newId('user', 1000) < ids.newId('user', 2000), 'ulids sort by time');
ok(ids.newId('user', 1000) < ids.newId('user', 2000), 'ulids sort by time');
ok(ids.parseSubject('user:42') === null && ids.parseSubject('user') === null && ids.parseSubject('svc:live') === null, 'malformed subjects are rejected');
ok(ids.parseSubject('user:42') === null && ids.parseSubject('user') === null && ids.parseSubject('svc:live') === null, 'malformed subjects are rejected');
ok(ids.principalSub({ type: 'service', id: 'live' }) === 'svc:live', 'service principal sub');
ok(ids.principalSub({ type: 'service', id: 'live' }) === 'svc:live', 'service principal sub');
-const nodeId = ids.newId('node');
-ok(nodeId.startsWith('nod_') && ids.principalSub({ type: 'node', id: nodeId }) === `node:${nodeId}`, 'node principal sub is node:nod_<ULID>');
-assert.throws(() => ids.principalSub({ type: 'node', id: 'nod_42' }), /principals/);
-ok(!ids.SUBJECT_TYPES.includes('node') && ids.parseSubject(`node:${nodeId}`) === null, 'a node is a principal, never a subject');
+const nodeId = ids.newId('link');
+ok(nodeId.startsWith('lnk_') && ids.principalSub({ type: 'link', id: nodeId }) === `link:${nodeId}`, 'node principal sub is link:lnk_<ULID>');
+assert.throws(() => ids.principalSub({ type: 'link', id: 'lnk_42' }), /principals/);
+ok(!ids.SUBJECT_TYPES.includes('node') && ids.parseSubject(`link:${nodeId}`) === null, 'a node is a principal, never a subject');
assert.throws(() => ids.principalSub({ type: 'user', id: ids.newId('user') }), /principals/);
assert.throws(() => ids.principalSub({ type: 'user', id: ids.newId('user') }), /principals/);
for (const [kind, prefix] of [['watch', 'wch'], ['observation', 'wco'], ['check', 'ckr'], ['job', 'job'], ['task', 'tsk']]) ok(ids.newId(kind).startsWith(`${prefix}_`), `newId(${kind}) is ${prefix}_<ULID>`);
for (const [kind, prefix] of [['watch', 'wch'], ['observation', 'wco'], ['check', 'ckr'], ['job', 'job'], ['task', 'tsk']]) ok(ids.newId(kind).startsWith(`${prefix}_`), `newId(${kind}) is ${prefix}_<ULID>`);
ok(contracts.validate('media.media-ref', { media_id: ids.legacyMediaId('live', 'vod', 42) }).valid, 'legacyMediaId is a valid MediaRef');
ok(contracts.validate('media.media-ref', { media_id: ids.legacyMediaId('live', 'vod', 42) }).valid, 'legacyMediaId is a valid MediaRef');
@@ -678,9 +681,9 @@ ok(V(sandboxTok, { acceptSandbox: true }).ok, 'sandbox app token accepted when t
@@ -678,9 +681,9 @@ ok(V(sandboxTok, { acceptSandbox: true }).ok, 'sandbox app token accepted when t
ok(V(serviceAuth.signServiceToken({ ...appClaims, env: 'production' }, kp.privateKey)).ok, 'production app token accepted');
ok(V(serviceAuth.signServiceToken({ ...appClaims, env: 'production' }, kp.privateKey)).ok, 'production app token accepted');
ok(V(serviceAuth.signServiceToken((({ project_id, ...rest }) => rest)(appClaims), kp.privateKey)).code === 'token.invalid_claims', 'an app token needs project_id');
ok(V(serviceAuth.signServiceToken((({ project_id, ...rest }) => rest)(appClaims), kp.privateKey)).code === 'token.invalid_claims', 'an app token needs project_id');
ok(contracts.ids.newId('project').startsWith('prj_'), 'project ids use the prj prefix');
ok(contracts.ids.newId('project').startsWith('prj_'), 'project ids use the prj prefix');
-const nodeClaims = { ...claims, sub: 'node:nod_01J0000000000000000000000Z', actor_type: 'node' };
+const nodeClaims = { ...claims, sub: 'link:lnk_01J0000000000000000000000Z', actor_type: 'link' };
ok(V(serviceAuth.signServiceToken(nodeClaims, kp.privateKey)).ok, 'node token accepted');
ok(V(serviceAuth.signServiceToken(nodeClaims, kp.privateKey)).ok, 'node token accepted');
-ok(V(serviceAuth.signServiceToken({ ...nodeClaims, sub: 'node:nod_42' }, kp.privateKey)).code === 'token.invalid_claims', 'a malformed node id is rejected');
+ok(V(serviceAuth.signServiceToken({ ...nodeClaims, sub: 'link:lnk_42' }, kp.privateKey)).code === 'token.invalid_claims', 'a malformed node id is rejected');
const agentId = contracts.ids.newId('agent');
const agentId = contracts.ids.newId('agent');
const agentClaims = { ...claims, sub: contracts.ids.principalSub({ type: 'agent', id: agentId }), actor_type: 'agent', cap: ['chat.presence.read'], cap_confirm: ['chat.message.send'], project_id: 'prj_01J0000000000000000000000Z', env: 'production', on_behalf_of: 'usr_01J0000000000000000000000Z', act: { sub: 'svc:actor' } };
const agentClaims = { ...claims, sub: contracts.ids.principalSub({ type: 'agent', id: agentId }), actor_type: 'agent', cap: ['chat.presence.read'], cap_confirm: ['chat.message.send'], project_id: 'prj_01J0000000000000000000000Z', env: 'production', on_behalf_of: 'usr_01J0000000000000000000000Z', act: { sub: 'svc:actor' } };
const agentTok = V(serviceAuth.signServiceToken(agentClaims, kp.privateKey));
const agentTok = V(serviceAuth.signServiceToken(agentClaims, kp.privateKey));