# Changelog ## 0.426.0 — 2026-11-13 **A benchmark run that did not measure cost, time or tokens says so** (plan 2026-11-13 §X, "missing stays missing"; an outside review found Bench's importers and mirror writing 0 for what they never had). `bench.run@2`: `cost_usd`, `ms` and `tokens` may be null, on the run and on each task; null means not measured, never 0. Bench #237 already stores null; 91 mirrored production runs were corrected from 0 to unknown on 11-13. Fixtures: valid `unmeasured-cost-time-tokens`, invalid `negative-cost`. ## 0.425.0 — 2026-11-13 **A feed card says who posted it and what a deal costs** (the owner, 11-13: "it should say who posted it, and it should show like … their level … more details of the user … their username effects"; "update the listings for deals"). `feed.item@1` 1.1.0, every new field optional: - `actor.handle` (the username, no @), `actor.level` (null when the person hides it) and `actor.fx` (the name effect they wear, an Inventory token such as `name-fx-rainbow`, looked up by the feed's server so no site needs Inventory in its connect-src; Shared's items.css draws it). - `group.items[].price` (`{ amount, currency }`), `was` and `store`: a folded run of deals lists each with its price, its old price and where it is sold. - Fixtures: `person-handle-level`, `deal-group-prices`; invalid `level-zero`, `price-currency-lowercase`, `fx-not-a-token`. ## 0.424.0 — 2026-11-13 **Agents have souls** (actor-characters.md; Design rules 175 `actor-is-the-work`, 176 `souls-grow-by-yes`). The owner, 11-13: "you should have different bots … how you make each character and they each have a soul … your agents are created based on your memories and personality". - **`agents.soul@1`** (`contracts/agents/soul.v1.json`): who an agent is in plain words: `who` (one line), four `dials` (warmth, play, length, edge, each -2 to 2), up to three `quirks`, up to five things it `believes`, how it does `pushback`, what it will `never` do beyond the floor, free `notes` (an imported SOUL.md's body), and how it was `made_from` (words, memories, gallery, twin, soul-md). It shapes the words, never the permissions, and holds no memory of a person: a copy carries the soul, never what was learned. - **`agents.blueprint@1`** gains an optional `soul` part (after `face`), by reference. Additive: every blueprint that was valid still is. - Fixtures: three valid souls; five invalid (a dial past two, six beliefs, no who, an unknown origin, a permission in the soul). ## 0.423.0 — 2026-11-13 - **Media lists `media.config.manage`** among the capabilities it enforces: Media now serves its settings to the owner's Setup at `/internal/config` (Media #441), so Network's setup and guide read it like every other service (the guide said "OpenVibe.Media did not answer"). The capability itself has existed since the owner settings proxy. ## 0.422.0 — 2026-11-13 **One evaluation loop: entrants, matches, and ratings that separate the model from the harness** (compare-arena-ecosystem.md). The owner, 11-13: the Arena "should still use our multi purpose shared universal agentic multi harness system", and comparisons should do "harness vs harness and model vs model … better". - **`bench.entrant@1`:** what competes. - Kinds: a `model` (on the neutral harness), a `harness` (over a fixed model panel), an `agent` (harness + model + settings, a blueprint version), or a `product`. - Stable ids: `model:…`, `harness:…@version`, `agent:harness+model#confighash`, `product:…`. - **`bench.match@1`:** one task given to two or more entrants under the same budget. - Each side carries its receipts: cost, tokens, TTFT, TPS, wall time, tool calls, and the length and markdown style covariates. - `shown` records the order the sides were displayed in; `blind` records whether names were hidden. - Judgements come from `human_pairwise`, `audience_poll`, `implicit` (merged, accepted, copied, kept, retried, stopped, edited), `test_suite`, `rubric` and `game_outcome`. A voter is a keyed hash with a quality weight, never an id. - Sources: arena, chat, codes, live, games, exchange, space, bench and check jobs. - **`bench.rating@1` 1.1.0** (all optional): - `entrant_kind` and `style_control`; - `factors`: an agent's strength split into model, harness and interaction, each with an interval; - `cost_per_solved_usd`, `tokens_per_task`, `ttft_ms` and `tps` percentiles, `reliability` (pass^k), `matches`, `as_of`. - **Event `bench.match.recorded` v1**, produced by Bench. - **`agents.play@1` 1.1.0:** `blind` hides the roles' names until the first judgement. This is the race play an Arena round runs on the agent engine. - **Fixtures:** - entrants: an agent, a harness, a model; - matches: an Arena vote, and a Codes race judged by its tests and then merged; - a rating with its factors; - invalid: an agent without its harness, a kind nobody has, a one-sided match, an unknown judge. ## 0.421.0 — 2026-11-12 **Every AI call, measured everywhere** (ai-metrics.md). - **`ai.call.recorded@1` 1.1.0** adds, all optional: - `operation` (chat, completion, embedding, image, image edit, speech, transcription, translation, rerank, moderation, decision, realtime, batch, tool); - `streamed`, and `tps` (the decode speed after the first token); - `queue_ms`; - `task`, `task_id` and `task_ms`, so cost and time add up per task; - `harness`, and `place` (provider, the person's machine, community, a plan, OpenVibe's hosts); - `error_class`, `fallbacks` and `cache_hit` (an exact-cache answer); - `tool_calls` and `region`. - **`ai.call-report@1`:** up to 100 calls a product made without OpenVibe.AI (Actor, Codes harness turns, Link's local models through Bot, Vibe). They go to AI's `POST /api/v1/usage/calls`. AI prices any that bring no price, records each once (by system + run or turn id) and publishes `ai.call.recorded@1`. The capability is `ai.usage.report`. - **`report.ai-performance@1`:** how calls performed for one group (model, provider, operation, system, task, place, harness): - success, fallback, streamed and cache-hit rates; - TTFT, latency and tokens per second at p50, p90 and p95; - tokens, charged and list cost, and cache savings; - cost and time per task; - the top errors. Public answers group by model, provider or operation, with at least 20 calls. ## 0.420.0 — 2026-11-12 **One agent system, measured.** These contracts carry the decisions in `one-system-v2.md`, which come from reading 24 open agent projects. - **`agents.blueprint@1` 1.1.0:** - `rules.sandbox` (read-only · workspace-write · full) and `rules.network` (none · public · listed domains): Codex's three axes per run, next to the level that was already there. - `starters`: one-tap first asks, Goose's activities. - **`agents.permission-rule@1`:** a permission as data, from Gemini CLI and OpenCode. - It has an action (a tool, or a pattern), a resource, an effect (allow · ask · deny), and `when`: attended, unattended or any. - The last rule that matches wins. - Unattended work never waits on an ask. - The floor asks whatever a rule says. - **`agents.job@1`:** one job placed on a machine. Its kinds are check, review, build, eval, improve, desk and model. It carries: - what it needs from the place, and its trust (public code only, or the owner's own machines); - its budget, its placement with the reason, and its attempts; - a result with a named exit and a link to the masked log. Secrets never travel in a job. - **`agents.run@1` 1.4.0:** `metrics`, with: - the charged cost first (an estimate only where nothing was charged); - tokens with cache reads and writes, latency and steps; - a named exit; - the checkable evidence of success (a test, a schema, a grader, a person, a Decision with its probability). - **Fixtures:** a blueprint with sandbox, network and starters; three rules; a check placed on a shared node, and a queued review; a run with metrics; and refusals for each. ## 0.419.1 — 2026-11-12 **`actor.agent.manage` lists the blueprint routes** Actor serves: the person's blueprints and the public gallery, one version, the draft, publish, remix, the install screen, install, and an installed agent's version (`agents.blueprint@1`, `agents.install@1`). ## 0.419.0 — 2026-11-12 **Make your own agent: `agents.blueprint@1`.** An agent anyone can read, change, share and run anywhere, as one small document of ten parts: - **face** (Design); - **brain** (Exchange): auto, a size or one model; the ladder rungs it may use; the most it pays; an advisor; Decisions; - **knows:** instructions with `{{inputs}}`, skills, knowledge, and examples for Try it and Bench; - **hands:** each tool allowed or ask first, plus tool search. The tools are `ov:` product tools, `tools:` apps, `connect:` apps, the installer's own `mcp:` servers, `machine:` files/shell/desk/browser/screen, `robot:` manifest commands, `harness:` coding harnesses and `agent:` other agents; - **places:** none, own, shared, openvibe or sandbox, and what the work needs; never a machine by name; - **memory;** - **rules:** level, budget, who may ask; - **when:** the Source kinds plus `event` and `schedule`; - **makes:** answers, an exact shape, and whether it is served to other agents over A2A or as an MCP tool; - **team:** a play whose roles are other blueprints at fixed versions. Inputs and needs sit above the parts. A blueprint carries no secret and grants nothing. A published version never changes. Design note: `agent-blueprints.md`. - **`agents.install@1`:** a person's own agent, made from one blueprint version. It records the inputs they filled, the asks they agreed to (by key) and the machines they chose. It says whether a version that asks for no more is taken without asking, and holds a `pending` version that waits for their yes. - **`agents.run-start@1` 1.2.0:** `blueprint` runs a version once without installing it (Try it). Every tool asks first. - **`agents.run@1` 1.3.0:** `agent.blueprint`, the version a run's agent was made from. - **Fixtures:** - five blueprints: Clip finder, Vibe as `bp_vibe`, a draft with a grader team, a tool for other agents, a robot driver; - ten refusals: full level without a budget, a tool nobody has, a schedule without a time, served as a tool without a shape, a team role without a version, published without a date, a key in the brain, a machine by name, an exact pick without a model, no instructions. ## 0.418.1 — 2026-11-12 **LOOI's manifest, exactly as the P7 list says it, and not frozen until Maticus has checked it.** The head has its own delivery window (1–300 ms; 500 ms is its lifetime on the Live tray, head-only profiles use 300), the top-level `battery` (0–100, polled) is a reading of its own, and `p7_battery`'s `unverified` is listed as a field that comes only with a read at connect or a stale value. `bot.robot-manifest@1` readings take `optional`: the fields that come only sometimes. 0.418.0 marked the example frozen before the maker's check; `frozen` is now false until he confirms it. ## 0.418.0 — 2026-11-12 **A robot's own manifest:** `bot.robot-manifest@1` (public, ADR-043) is what a robot's driver says the robot can do, written and versioned by the driver's maker, so a new control is a version bump of the manifest instead of an OpenVibe release (the plan in Maticus's LOOI thread, 11-10; his P7 command list on 11-12 is its first frozen version). - **Commands:** kind, class (`motion`, `state`, `action`), the value's fields and ranges, `lifetime_ms`, the `delivery_ms` window Bot gives it, `min_left_ms` on arrival, `rate_per_s`, `release` (`stop`, `hold`, `none`; motion must stop or hold), `latched`, the telemetry key that `confirm`s it, and the `control` the panel draws (stick, slider, toggle, button, pad). An actuator names which one. - **Telemetry:** a key, its fields (ranges, `boolean`, `string`, `ms`, `integer`, `number`, or a list of values), unit and range, `source` (`poll`, `push`, `connect`), `fresh_ms`, and its control (meter, readout, badge, list). - **`frozen`:** true once the maker and OpenVibe have checked a version against the driver's real command list. - **`ui.surface@1` parts say what they send or show:** optional `command` and `reads`, in the manifest's terms, so OpenVibe AI knows a part's ranges, lifetime, release and rate when it arranges a panel or writes a control. They grant nothing: the product's gate still checks every command. A new example is LOOI's panel built from its manifest. - Fixtures: LOOI 1.0.0 through the P7 tray (drive 500 ms life, delivery 1–300, release stop; head 500 ms, hold; headlights delivery 1–250, 50 ms left, 2 a second, latched; battery by poll with `at` in ms; who is driving), and three refusals (motion without a release, an actuator without its name, motion that is never released). ## 0.417.0 — 2026-11-12 **The agent engine's next shapes:** who asked, what a run continues, how several agents share one ask, and where the money went (agent-engine.md §§3-5, from the code study). Every field is optional and the new contracts are new. - **`agents.run@1` 1.2.0:** - `mandate`: an ask taken in from the feed, a room, an issue: where, by whom, explicit or inferred, and the scope; - `lineage`: a fork, a hand-off or a regenerated turn, with the run and turn it came from; - `play` and `role`; - `agent.capabilities`; - `cost.by`: the spend on each rung of the ladder (machine, plan, key, allowance, credits). - **`agents.step@1` 1.2.0:** `turn`. - **`agents.run-start@1` 1.1.0:** `play`, `lineage`, and `mandate` (from intake only). - **New `agents.play@1`:** a play as data, made of roles, phases, checks, a stop rule and a budget split. Fixtures: advisor, grader loop, fan-out. - **New `agents.source@1`:** one place asks are taken in: who may ask, what counts as an ask, the run it becomes, its budget, and how the run answers. Fixtures: the owner's feed, Vibe in a room. - **Stop is a plain control.** On small hobby robots, the panel's Stop is one ordinary, optional control that sends the machine's own halt: - `bot.robot@1`: the description and the old latch field say it is legacy (always false); schemas are unchanged. - `bot.device-message@1`: the old latch frame and the `estop_*` fields are described as legacy names devices may still send, not as a remote latch; Bot's config always sends the latch false. - `bot.command-result@1`, `bot.robot-control-result@1`, `bot.robot-manage-result@1`, `bot.command.refused@1`: the old stop endpoint and refusal code are gone from the descriptions. - `bot.estop.set@1` and `bot.estop.cleared@1` are retired (fixtures and schemas stay for the contract window). - `ui.surface@1`: Stop is not a required part; `ui.action@1` names Stop alone; `ui.trigger@1` adds `stop`. - Manifests and ADRs use Stop; `bot.resource.control` stop sends halt and resume is a calm no-op. ## 0.416.0 — 2026-11-12 One view API for every content kind (the owner, 11-12): a preview in the feed and a watch on a page count the same item once, and Report can say how many people watched and how many stayed. - Add `media.view-request@1`: `POST /api/v1/views` { ref: { service, kind, id }, how: preview | watch | live, seconds, of_seconds?, session } on OpenVibe.Media (batched by `{ events: [ … ] }`, at most 20), with the preview/engaged thresholds and the 30-minute per-viewer window. - Add the `media.viewed` and `media.engaged` events: the counted view and the first engagement for an item, with the optional Report dimensions site and creator and no IP, user agent or raw session id. - Fixtures for both events and the request, and the generated JSON Schema and TypeScript. ## 0.415.0 — 2026-11-12 One feed for all of OpenVibe: the card OpenVibe.Network serves from /api/v1/feed and /api/frame/feed, and that OpenVibe.Community's Pulse reads instead of its own collector. - Add `feed.item@1`: each card's stable id, kind (post, pic, clip, video, live, stream, rundown, quest, member, chat, event, wiki, update, work, deal, news, report, poll, game, meme, paste, episode, space, avatar, banner, follow, site), link, source site, actor and subject, first picture, counts (views, likes, comments, watchers, people), live_now, a folded group of the same kind, a report trend, starts_at and follower visibility. - Fixtures for every kind, plus invalid ones (unknown kind, missing id, a URL without a scheme, a negative count, a subject that is not a usr_ id, private visibility, a one-item group, a trend without a direction), and the generated JSON Schema and TypeScript. Additive and new: no existing contract changes. ## 0.414.0 — 2026-11-12 The whole run (the agent engine, `docs/agents-runs.md`; design `agent-engine.md`). The owner, 11-12, with the menu's Chat · Code · Agents switch crossed out: "one new unified agent … the same agentic system, shared across agents, code and chat". Everything a person asks an AI to do is one shape: a run, and its thread of steps. - `agents.run@1` 1.1.0, optional fields only: - `title`, `kind`, `ref` (its address on the Frame), `url`; - `model`, `where`, `target`; - `doing`, `waiting`, `preview`, `counts`, `ended_at`; - `context`, `outputs`, `visibility`; - the agent's face: `agent.glyph`, `agent.hue`, `agent.avatar_url`, `agent.harness`. - `agents.step@1` 1.1.0, optional fields only: `from`, `author`, `status`, `title`, `glyph`, `attachments`, `sources`, `ui` (a rich card) and `desk`. - Add `agents.run-start@1`: what starts a run on any product and on the Frame. Only `text` is required. - Add `agents.event@1`: a run's stream (`run`, `step`, `delta`, `approval`, `end`). - Add the event `agents.run.updated` (`@1`): the run's snapshot on OpenVibe.Events when what a list draws changed. - A conversation is a run that is `waiting` for a `message`; an approval and a takeover are the same status with another `for`. ## 0.413.0 — 2026-11-12 The composer's Reach (Post · Ask · Do, stage R3; Network R3): what its `/` and `@` menus offer. - Add `frame.commands@1`: `GET /api/frame/commands?site=&q=` on OpenVibe.Network, the command rows a composer offers on one site — the network's `/ask`, `/do`, `/remind` and `/timer`, the site's own from its Frame manifest, and a signed-in person's saved composer snippets — each with its hint, the text a pick inserts, its kind and whether it needs a signed-in person. - Add `frame.mentions@1`: `GET /api/frame/mentions?q=&site=` on OpenVibe.Network, at most eight rows for the `@` menu: public people (with a handle, display name and an OpenVibe.Media avatar only), the person's own agents from the agent directory, open OpenVibe sites and Live's public channels. People first; signed out, public people only; no email or private account field. - Fixtures for both (valid and invalid) and the generated JSON Schema and TypeScript. ## 0.412.0 — 2026-11-12 One agent system, stage 2: one run shape for Actor, Codes and Run, and the step event that follows it. - Add `agents.run@1`: a run's id, owner, agent and product, parent run, budget (USD, tokens, seconds and steps), status (`planning`, `running`, `waiting`, `done`, `failed`, `stopped`), created and updated times, a result summary and what it spent. One shape for OpenVibe.Actor, OpenVibe.Codes and OpenVibe.Run. - Add `agents.step@1`: one entry of a run's append-only log (run id, seq, kind `think|tool|message|approval|child|file`, text, the tool call and its input, the result, the step's cost, start and end, and the child run it started). - Add `agents.approval@1`: an ask attached to a run's step (what, the options with their labels, who decided and when, and the answer). - Add the event `agents.step.appended@1`: the appended step as the payload, on OpenVibe.Events. The `agents` namespace is hyphen-free because an event type cannot carry a hyphen; Actor registers the producer. - Add valid and invalid fixtures for all four, the generated JSON Schema, TypeScript and per-service OpenAPI, and `docs/agents-runs.md`: which field of Actor's `tasks`/`task_events` and Codes' `run_tasks`/`codes_session_lines`/ `codes_approvals` becomes which field of the new shape, and what does not fit (Actor's `needs_you`, per-step cost, split tool call/result entries, and Codes' attempts as one flat run). ## 0.411.1 — 2026-11-12 - Add the `code` category to `sources.source@1`, `sources.item@1`, `sources.category@1` and `sources.source-write-request@1`: GitHub repositories (trending, top, new and star-gain rankings) enter OpenVibe.Sources under one category, with a 30-day item window. Additive: no existing field changes, and an older reader ignores the new enum value. ## 0.411.0 — 2026-11-12 The composer's Frame (Post · Ask · Do; Network #858): what its Model sheet lists and what an Ask sends. - Add `frame.choices@1`: `GET /api/frame/choices?task=` on OpenVibe.Network, the models a composer offers for a task in groups (OpenVibe's, the person's keys, computers and plans), each row with its maker, price per million tokens in and out, speed, Report score and its Exchange page, plus the recommended and current choice. - Add `frame.ask@1`: the body of `POST /api/frame/ask`, the question, the model (a row id or `{ model, source, machine }`), the sources to search (`web`, `openvibe`, `mine`, `page`), the depth and the files and page context it carries. - Fixtures for both (valid and invalid) and the generated JSON Schema and TypeScript. ## 0.410.0 — 2026-11-11 A train of three pull requests: the layouts of make-your-own-controls (#455), Elements stage 1 (#456) and the deep dive (#457). - Add `ui.surface@1` (the catalog of a surface's parts, their minimum sizes, styles, bindings and `never_covers` rules), `ui.layout@1` (a person's arrangement on one device class, with its name, author and sharing), `ui.layout-request@1` (catalog, current layout, words and device for the AI task) and `ui.layout-suggestion@1` (a validated layout with a one-sentence summary, reasons and the model's receipt, or an honest refusal with the validator's reasons). - Add `network.layout.changed@1`: the event Network writes in the transaction that sets, saves, renames, deletes, shares, publishes, copies or resets one person's layout. It carries the surface, device, layout id, change and revision, never account data or layout contents. - Add `ai.layout.suggest` (first-party, `POST /api/v1/ui/layout/suggest`) and `network.layout.read` (first-party, `GET /api/v1/people/:subject/layouts/:surface`), and list them in the AI and Network manifests. - The fixtures cover a bot-controls catalog, a phone layout, a docked overlay, a suggestion and a refusal, a request and the event; the suite checks part ids, theme tokens, never_covers targets and the 44 px minimum. (#455) - Add `ui.element@1`: a look from OpenVibe.Design's components and the theme's tokens, a recipe (triggers, conditions, actions), the capabilities the element declares, an optional mod reference (id, version, digest) and its sharing. The store assigns the id and revision; a draft has neither. - Add `ui.trigger@1` (press, hold, double-press, key, gamepad, chat command and text, follow, tip, AI Moment, Rundown topic, timer, schedule, robot telemetry and event, stream start and end, viewer count, another element's event) and `ui.action@1` (robot, chat, screen, stream, AI, Actor, webhook and tip-goal actions), each action carrying the product that authorises it. `ui.action@1`'s `$defs.capability` is the one list an element's `capabilities` draws from. - Add `ui.element-state@1`: the element's own store (counters, scores, lists), with a revision that grows per accepted change and values that are booleans, numbers, short strings or lists of them, never objects. - Add `live.element.pressed@1` and `live.element.state@1`: a press with the presser's public name and the trigger, and the store with its revision, written by Live. - Fixtures cover a gallery dance button, a chat counter, a mod element, trigger and action variants, the state, and both events; `test/elements.test.js` holds the capability list to the action catalog, each kind to its product, and the recipe to the element's declared capabilities. (#456) - Add `ai.deep-dive@1`: a deep dive run's report as OpenVibe.AI's `design.deep_dive` workflow produces it for OpenVibe.Exchange and OpenVibe.Design — the question and depth, the seven steps, every source, every capture (a private Media object per viewport and theme, with its DOM facts and Expert extras), the per-site analysis, the synthesis, the recommendations checked against OpenVibe's rules, and the receipt. Every claim carries its evidence. - Add `ai.deep_dive.updated@1`, the event AI writes on each step transition and at the end, so a page can follow a run without polling and Design can attach a finished report. (#457) ## 0.409.0 — 2026-11-11 - Define machine access requests, grants and signed token claims; the agent directory, A2A tasks and results; five Network events and four registration and relay capabilities. ## 0.408.0 — 2026-11-11 The rest of the 0.407.0 train. v0.407.0 was tagged at #451's merge, before the train merged, so the tag holds #451 alone; a tag is never moved, so the three pull requests the train carried are released here. - Add `codes.task-summary@1` (a task as the Dock's Code tab lists it: id, title, status, mode, repo, times, attempts, url, preview and PR links) and `codes.task.updated@1`, the one event Codes writes on every task status change beside the per-state `codes.task.*` events (#425). - Add `compare.comparison@1`: things, weighted criteria, finite results with required observation dates and HTTPS sources, method, source list and update date; register the Compare service, repository and product in Read & ask (its group gains an eighth tile) (#428). - Add the optional `is_test` to `identity.subject-projection@1` so public surfaces can leave probe and test accounts out while their own signed-in views keep working, and the `{ subjects }` answer of Network's `GET /internal/identity/test-subjects` in `identity.resolve-result@1` (#450). ## 0.407.0 — 2026-11-11 - Add `exchange.search.icons`: a first-party service searches icon references through the Exchange platform API (`POST /v1/search/icons` at `api.openvibe.exchange`, served by OpenVibe.AI) with its own free-search quota and no Exchange key. It reaches the icons route only, never web, image or fetch search, and the receipt names the engine, cost and skipped attempts. OpenVibe.Design holds it for the references shown beside a glyph draft. (#451) ## 0.406.0 — 2026-11-11 The second train of the day, for the five pull requests opened after 0.405.0 (one line each): - Define `meme.caption.created` for a signed-in person's public caption, with only its id and owner (#444). - Add `codes.model-plan@1`: execution modes, role models and effort, bounded samples and budgets, an escalation ladder and required checks; task requests accept a plan or a preset, and task reads can include the resolved plan and per-role usage and outcome (#445). - Delivery and retry on computer and browser results: `bot.command-result@1` and `run.session-control-result@1` gain `delivery` (`delivered`, `not_delivered`, `uncertain`) and `retry` (`safe`, `reason`), only `not_delivered` is safe to replay; optional browser `origins` on browser commands, Run session creation and Bot desk lease creation (#446). - Add `ai.model-directory-entry@1` and `ai.model-directory-list@1` for public model listings, with provider-listed models routed off, preview and retired models, and sourced pre-list sightings; a spotted entry never implies availability (#447). - Keep the exact reply target and its display name in Community comment reads; document the two-reply preview and four Chat-compatible file references on comment writes and reads (#448). ## 0.405.0 — 2026-11-11 This release train carries the contract work that was waiting on the owner's release. One line per carried pull request: - Add `live.layout@1`, the player layout a person keeps in their account: per stream slot and source, the place as fractions of the frame, mode `dock` or `float`, the tuck side, mute and the time, at most 60 places (#442). - Add `chat.command-registration-request@1` and `chat.command-registration-result@1` with the active capability `chat.commands.register`, so a consented app or an Actor agent can register a plain response command for a server its person manages (#441). - Catalog OpenVibe.Directory as a live service and site, replacing its parked product metadata, with its public origin, source refresh lifecycle and public-listings-only scope (#440). - Add `actor.agent-sheet@1` and `codes.agent-sheet@1`: the owner-scoped agent sheet responses, with current work, daily activity, approvals, computer, schedules and memory where recorded (#439). - Extend `community.feed-item@1`, `community.pulse-item@1` and `community.pulse-write-request@1` with activity kinds, followers-only visibility, scheduled starts, previews and links to every item in a folded burst; Community consumes Network profile updates (#438). - Add `chat.person-memory-result@1` and `chat.person-memory-preferences-result@1`: the public and private parts of Chat's compacted day, week and month memory, and the erase and opt-out responses (#437). - Add `preview_url` (and `preview_webm_url` on media) to `media.vod@1`, `media.clip@1` and the Live stream shapes: the 6 s muted hover preview beside a thumbnail (#436). - Add `agent.ui-event@1` with the AG-UI 1.0 event union and OpenVibe `metadata.ov`, `agent.run@1` snapshots, and the `agent-run` chat embed (#434). - Add `network.user-card@1` with viewer-aware actions, and `network.user-report@1`, `network.user-report-request@1` and `network.user-report-list-result@1` for a person's reports and the staff queue (#433). - Add `community.feed-card@1` and `community.feed-answer@1`, with preview media support (#432). - Add `composer.attachment@1`, `composer.prefs@1` and `composer.mention@1`, the shared composer's server-side shapes (#426). Valid and invalid fixtures cover every new shape, and the generated TypeScript, JSON Schema bundle and per-service OpenAPI files are regenerated at this version. ## 0.404.0 — 2026-11-11 - Add owner-only Run compute configuration capability for Network admin proxy. ## 0.403.0 — 2026-11-11 - Extend `actor.task-create-request@1` with conversation, tools, model and optional content tier fields for Actor turns; uncensored content is available only when a task names the person it is for. - Deprecate the account-merge contracts after removal of the feature across services; retain the released schemas. - Add `chat.embed@1` as the shared address preview shape for chat clients. ## 0.402.0 — 2026-11-11 - Activate `download.file.read` for grant-bound, per-file reads by agents on openvibe.download; the capability alone does not provide drive listing or access without an owner's active grant. ## 0.401.0 — 2026-11-11 - Add the `live.channel_points.read` capability (with `live.channel-points-page@1`): OpenVibe.Network reads one channel's points moves for the person behind a Network account through Live's internal channel-points changes read, for the gain/loss API (ask 273). ## 0.400.0 — 2026-11-10 - Regroup the sites into the owner's eight groups (plan ask 274, 2026-11-10): `site.neighbourhood` is now `watch`, `talk`, `play`, `read`, `shop`, `tools`, `make` or `build`, the same ids and order as OpenVibe.Network `frame/sites.js` GROUPS and OpenVibe.Shared `rooms.js` HOODS. Every site manifest carries its new group; merged sections point at the site that shows them (Media → Video/watch, Bench → Report/read, Play → Games/play). ## 0.399.0 — 2026-11-10 - #408: add `quest.today@1`, the private Quest today response. - #412: extend `network.operator-alerts-request@1` with browser-check evidence and release timing details. - #415: add the `git.checks.write` capability for reporting commit statuses. - #416: add `agent.finding@1` for confirmed findings, leads that need validation and rejected claims. ## 0.398.0 — 2026-11-10 - `codes.task@2`'s outcome state names a goal task's honest end state: `ready` only with an accepted independent verdict, `waiting`, `blocked` at its no-progress limit, `failed`, `dropped` by the person, or `stopped_by_budget`. A task without a goal keeps `done`, `unfinished` and `stopped`. ## 0.397.0 — 2026-11-10 - Add `agent.goal@1`: a task's outcome in words, a typed check (`command`, `url` or `rubric`), optional resource budgets (tokens, turns, active minutes) and a no-progress limit, plus the worker's bounded completion proposal and evidence and the independent verifier's verdict (accepted or rejected, with a reason and the check's own output). - `codes.task-start-request@1` takes the optional `goal`; `codes.task@2` carries it back with the worker's proposal, evidence and the verifier's verdict. ## 0.396.0 — 2026-11-10 - Add optional ordered attempts to web and image search receipts, with provider/engine, status, safe reason codes, latency and cost. Existing receipts remain valid. ## 0.395.0 — 2026-11-10 - Add two planned products to the catalog: `manifests/products/openvibe.compare.json` (OpenVibe.Compare: compare anything side by side with its sources and dates, AI models first) and `manifests/products/openvibe.directory.json` (OpenVibe.Directory). Both are `noRepo` with launch notes; the addresses are parked. Compare's "meanwhile" link is OpenVibe.Exchange, where the models and their prices live today. ## 0.394.0 — 2026-11-10 - Add `chat.room-insight-result@1` (`contracts/chat/room-insight-result.v1.json`): the public answer of `GET /api/v1/rooms/:id/insight` on OpenVibe.Chat. One shape for every room: `overview` (the text written for someone arriving now, its topics with message counts, how many people and messages the window held, its from/to times and when it was written), `timeline` (the room's notable moments, newest first, each with `at`, `kind`, `title`, `text` and the `message_id` it came from), and `state` (`ready` / `quiet` / `off`). Quiet is explicit: an old or empty summary carries no text rather than invented news. The overview reads only people who allowed AI use of their messages (`network.consent@1`, purpose `ai.train` on kind `chat`). ## 0.393.0 — 2026-11-10 - `billing.money-reason@1`: declare `donation:received`, the row a creator sees for a tip that arrived ("for a tip from @alice"). The kind list is one list; the sender's `donation` still reads "for a tip to @bob". ## 0.392.0 — 2026-11-10 - Add `billing.money-reason@1` and `lib/money-reasons.js`: one list of the plain words for a movement of OpenCoins, Vibes or Balance. Each kind declares the key it matches (`quest:publish-a-video`, `Media request: `, Billing's type:metadata), its site, what its subject is (a quest, a stream, a tip, a creator, an app, a project, a purchase), the phrase that names it ("for the quest {name}"), and how the subject's own name and address are found. `describe()` answers `{ key, text, subject: { name, url } | null }`, so a page writes "+15 OpenCoins" before the text and links it; an undeclared key reads "for something on <site>" with `known: false` and never shows the key. Fixtures cover a quest, a tip, a reason with no page and an undeclared key. ## 0.391.0 — 2026-11-09 - One Network experience curve, named reasons, level reads and capped service awards. ## 0.390.0 — 2026-11-10 - A service manifest's `site` block takes an optional `strip`: up to three items a site adds to the network's footer strip, read with the rest of it at OpenVibe.Network `GET /api/frame/online?site=<id>`. An item is a plain label, an icon from openvibe-shared's glyph set, an https link on an `openvibe.*` domain, and either a `count` the site reports or a fixed `line` — never both. Live, Community and Games declare one each as the worked examples. ## 0.389.0 — 2026-11-10 - Add `ai.advisor@1`: how a run may ask a stronger model for advice during the run. Off when the request omits it; at most 3 uses and at most 100 words of advice; the advisor may be named, the effort is concrete, and each use is on the run's receipt with its cost. Add `ai.advice@1`: one use on that receipt, naming the advice run, the model, the tokens and the cost. `ai.run-request@1` takes the optional `advisor` field and `ai.run@1`'s explain carries the uses. ## 0.388.0 — 2026-11-10 - Add `git.clone-request@1`: one "Clone to my computer" request from a repository page. Git mints it for the signed-in person, the person's Link reads it over an outbound HTTPS relay, asks for approval on the machine, clones into a folder inside the root the person chose in Link, and reports progress back; the page polls the same document. The request lasts two minutes, only a repository the person may read can be requested, and `clone_url` (with a short-lived read token) is present only in the Link's answer, never in the page's copy. - Add `git.machine@1`: a computer published to OpenVibe.Git through Link, read-only. It lists the repositories under the root the person chose in Link, each with its branch, head commit and date, and whether the machine is online ("offline since …" when it is not). Browsing is asked of the machine over the poll it opened; nothing in this contract is a write path. - Valid and invalid fixtures cover a pending, cloning and done request, a bad id, a bad state and a traversing repository name, and an online and offline machine, a machine without repositories and a repository name that escapes its root. ## 0.387.0 — 2026-11-09 - Add `ai.creation@1` and `ai.creation-list@1`: the catalogue of everything AI can make on OpenVibe, one entry per creation. An entry names the product it lands in, a plain one-line name, what the person gives it and what comes back, the workflow or registered task behind it, who may use it (guest, signed-in, the 18+ floor), whether it can cost coins, and the URL where a person starts it. `GET /api/v1/creations` on OpenVibe.AI answers the list, built from the registered workflows and tasks so only what is live is listed; the contract is public and its fixtures show a workflow creation and a task creation. ## 0.386.0 — 2026-11-09 - Add `chat.command@1` for registered slash commands and typed options. ## 0.385.0 — 2026-11-09 - Add `ai.file@1` plus typed conversational turn request and result shapes for Media-backed attachments and artifacts. ## 0.384.0 — 2026-11-09 - Agent work fabric: trigger, decision, immutable ticket mandate and budget, six report states, attention, and ordered stage vocabulary. Portable AI effort, concrete task-default effort, and optional effort/ladder receipt metadata. ## 0.383.0 — 2026-11-09 - `ai.provider@1`: measured per provider+model circuit health (status, failure class, plain reason and since when, retry/backoff, notify time); `ai.run-read-result@1`: `requests[].error_class`. ## 0.382.0 — 2026-11-09 - Add `exchange.market@1`: paginated own-page market offers, routing flags and per-field model metadata provenance. ## 0.381.0 — 2026-11-09 - blog.changelog-entry@2: a shipped change with its plain words, kind (new, better, fixed or internal) and one to three areas from Blog's fixed list, with the commit and pull request for the row's fold ## 0.380.0 — 2026-11-09 - Exchange routing controls, owner request presets and generation accounting schemas, with valid and invalid fixtures. ## 0.379.0 — 2026-11-09 - Wiki's rich articles: the page write request and the page read result carry `blocks` (`publishing.block@1`) next to the Markdown, and `wiki.images-suggest-request@1`/`-result@1` and `wiki.article-draft-request@1`/`-result@1` describe the image suggestions (`wiki.article.images`) and the `content.wiki` draft revision that waits for a person. ## 0.378.0 — 2026-11-09 - OpenVibe.Connect and the relay profile describe the Link, not the Node. ## 0.377.0 — 2026-11-09 - community.feed-item@1: live_now on a live stream's row ## 0.376.0 — 2026-11-09 - git.change@1, git.issue@1 and the review events for OpenVibe.Git's changes and issues (G2) ## 0.375.0 — 2026-11-09 - AI tasks and choices: `ai.task@1`, `ai.choice@1`, `ai.task-defaults@1`; a run accepts `task` or `choice` beside `workflow`, and `run.explain.task_choice` says what answered. `ai.me-preferences@1` gains `defaults.search`. ## 0.374.0 — 2026-11-09 - Add the Pics public gallery and picture contracts (`pics.gallery@1`, `pics.picture@1`) with valid and invalid fixtures. ## 0.373.0 — 2026-11-09 - The git service: its manifest, git.repo.read and git.repo.write, git.access-request@1 and git.access-result@1 (OpenVibe.Git) ## 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 ## 0.370.0 — 2026-11-09 - design.component@1: one platform-neutral definition per shared interface (the public registry on openvibe.design) ## 0.369.0 — 2026-11-09 - OpenVibe Git: git.repo@1, git.commit@1, git.tree@1 and git.insights@1 ## 0.368.0 — 2026-11-09 - The wallet and the reviewed store: billing.wallet@1, inventory.store@1, inventory.store-listing@1, inventory.purchase@1 ## 0.367.0 — 2026-11-09 - publishing.block@1: the rich content block of a post or an article body ## 0.366.0 — 2026-11-09 - wiki.page.published@1 and wiki.page.updated@1 carry an optional `title` (the page as readers see it), so the network's Feed names a wiki page by its title, not its slug. ## 0.365.0 — 2026-11-09 - Dock v2: action-item words, ai.run-error@1, skill runs and Vibe conversations ## 0.364.0 — 2026-11-09 - The Feed of everything: community.feed-item@1 ## 0.363.0 — 2026-11-09 - npm metadata and CITATION ## 0.362.0 — 2026-11-09 - Your stats: report.me-stats@1 and report.me-stats-publish@1 ## 0.361.0 — 2026-11-09 - Reviewed glyph draft service ## 0.360.0 — 2026-11-09 - Meme entry and render contracts ## 0.359.0 — 2026-11-09 - Media multipart uploads in contracts and OpenAPI - Define `design.component@1` for web, Android and iOS component anatomy, props, states, tokens, behavior, accessibility and examples. ## 0.358.0 — 2026-11-09 - Chat view preference (bubbles or room) ## 0.357.0 — 2026-11-09 - Cam camera settings (cam.settings@1) ## 0.356.0 — 2026-11-09 - Shoes product contract (shoes.product@1) ## 0.355.0 — 2026-11-09 - Daily quest contracts and step contribution ## 0.354.0 — 2026-11-09 - Vibe compare and Exchange chat export contracts (the Chat move) ## 0.353.0 — 2026-11-09 - Add `actor.ask@1`: durable approvals, questions, personal steps and sign-in hand-offs, linked to the person’s Actor and goal, with evidence and an answer or decline. ## 0.352.0 — 2026-11-09 - The menu's home screen and wall (M1): `network.home-screen@1`, `network.menu-feeds@2` (order and off; v1's on reads as the start of the order), `network.frame-today@1`, `community.status@1`, `community.pulse-contribute-request@1` and the person-granted capability `community.pulse.contribute`, with fixtures and the manifests' routes. ## 0.351.0 — 2026-11-09 - Add the five public Report AI comparison response contracts and fixtures, and list their cached public routes in the Report service manifest. ## 0.350.0 — 2026-11-09 - Define credential-free blind fill request, encrypted grant, and outcome contracts, plus the `run.fill.approve` capability. ## 0.349.0 — 2026-11-08 - Add `identity.emails@1` and `identity.email-add-request@1` and the `identity.emails.read` capability for a person's verified, privacy-limited email addresses. ## 0.348.0 — 2026-11-08 - Service manifests: `studio` (OpenVibe.Studio at openvibe.studio, live: Photo and Light in the browser), and `connect` lives at `https://connect.openvibe.link` (its home on OpenVibe Link since 11-08; `connect.openvibe.website` stays as the second domain). The registry still gave the old origin, so the sign-in canary read a 302 for Connect. ## 0.347.0 — 2026-11-08 - Add `identity.trust@1` and `identity.trust-request@1` for account and guest abuse signals, plus the app-grantable `identity.trust.read` capability. Responses contain coarse age bands and aggregate counts only; IPs, emails, last-seen timestamps, service user ids and linked account identities are forbidden by the schema and fixtures. - Service manifests: `locker` (OpenVibe.Locker at openvibe.locker, live; it owns the `locker` namespace), and `computer` is live (its exposure was still "repository"). ## 0.346.0 — 2026-11-08 - `actor.skill-listing@2` (2.1.0) gains `visibility` (plan HO11, the agent writes its own skills): `public` (the default, and what every listing is today) or `private`, meaning only its publisher sees or installs it. Additive: the field is optional, so a listing without it stays public and nothing that exists today changes shape. A private listing's `publisher.kind` must be `person`: OpenVibe's own skills are always public. JSON Schema states both the enum and that rule, and the fixtures hold it. ## 0.345.0 — 2026-11-08 - Add `actor.approval-answer@1` (plan HO12, the phone as the agent's eyes and hands): the person's answer to an approval that asked for the phone's camera or its location. The person does the thing themselves, and the answer carries the result back to the step as untrusted input. `decision` is `answer`, and exactly one of: - `photo`: `media_id`, a private Media object the person took in the system camera (JPEG, PNG or WebP, at most 10 MB, EXIF stripped by Actor); the step reaches it through a short-lived signed URL; - `location`: `lat` -90 to 90, `lng` -180 to 180, `accuracy_m` 0 to 100000 metres and `precise`. A coarse answer (`precise` false) carries `lat` and `lng` to at most two decimals; JSON Schema cannot count decimal places, so Actor rounds the share and the repository's suite checks the rule. The answer has no `secret`, `token` or `url` field, and a decline stays the ordinary denial. - `actor.skill@1` (1.1.0) and `actor.skill@2` (2.1.0) gain `device.camera`, `device.location` and `device.notify` in `requires.kinds`, so a skill's steps may be one of those approvals like any other HO1 kind. Additive: nothing that exists today changes shape. ## 0.344.0 — 2026-11-08 - `actor.trigger@1` (1.2.0) gains the mention kind (plan HO13, standing intents): a trigger that waits for the person to say one of their phrases in their own conversation with their own agent. Additive: - `kind` may now be `mention`; `when.mention` holds `phrases` (one to five, 2 to 80 characters each, trimmed) and `match` (`any` or `all`), while `when.words` stays the card's line ("When you mention the release"); - `fires` may only be `once_per_conversation`: the trigger fires at most once in each conversation; - `expires_at` is required for this kind and at most 90 days after `created_at` (the default is 30). JSON Schema cannot compare two dates, so Actor enforces the cap, and the repository's suite checks the fixtures and the rule. - A mention trigger listens only to the person's own words to their own agent: never anyone else's messages, never a room or a stream chat, and never the agent's own answers. Matching is case-insensitive, on word boundaries, after Unicode normalisation. Nothing that exists today changes shape. ## 0.343.0 — 2026-11-08 - Add `actor.skill@2` and `actor.skill-listing@2` (ADR-060, plan HO8): a skill may now bring code that runs in a sandbox instead of only a prompt. `runs` is `prompt` or `sandbox`; a sandbox skill carries 1 to 5 JavaScript `tools` (name, description, `entry` naming one of its resources, a JSON Schema subset for `args`, `net` `deny`/`openvibe-only`/`public` default `deny`, and limits under 30 s / 256 MiB), and the code itself is an ordinary text resource (`.js`, at most 64 KiB) in the same integrity hash. `requires.kinds` gains `skill.run`. The v2 listing pins each code resource's Media id and sha256 and lets a scan finding name the tool it is about. Code never runs on an OpenVibe server, never outside a sandbox and never with a secret: it runs as a Run `function` job on a Link that declared the skill runner. `actor.skill@1` and `actor.skill-listing@1` are unchanged and remain what a prompt-only skill publishes. ## 0.342.0 — 2026-11-07 - Add `mail.agent_inbox.received@1` (track HO6, channels): a message arrived at an agent's own address and the person has switched that address's answering on, so their agent may read it. The payload names the inbox and the agent, the owner, the mail server's message id, the sender, the subject and a plain-text excerpt of at most 4000 characters. It never carries the attachments, the HTML or the whole body: the agent's system reads the text through `mail.agent.read`, on the owner's behalf, once it has decided to answer. Mail publishes nothing for a sender on the person's block list or a message the mail server marked as spam, and nothing at all until the person turns the agent's switch on. - Add `mail.agent-inbox-message@1`, the one message an agent reads: the sender, the addresses the mail server stamped at delivery, the subject, the text cut to 4000 characters, and the threading headers a reply needs. Never the HTML, never an attachment, and never a sender-written Delivered-To. The message is data from a stranger, and any instruction in it is that stranger's. - Add `mail.agent-reply-request@1` and `mail.agent-reply-result@1`, an agent answering one such letter: the body is plain text, the recipient is the sender of the message being answered (the same address may be named, never another), and the reply carries In-Reply-To and References so the thread stays one thread. The result says whether the mail server took it — `sent`, not "delivered", since a later bounce is not knowable at that moment. - Add `mail.inbox-answering@1`, the switch behind all of it: off by default, per agent, with when the person last set it. - Add the capabilities `mail.agent.read`, `mail.agent.reply` and `mail.agent.answering`. The first two are loopback-only and first-party: only a token that names the person (`on_behalf_of`) and the agent's own inbox reads a letter or sends a reply, and only OpenVibe.Actor is granted them. The third is the person's own switch, under the `mail.addresses.write` a caller already holds. The `mail` service manifest lists all three and now produces `mail.agent_inbox.received`. - Additive only: five new contracts, three new capabilities and one manifest field; nothing that exists today changes shape. ## 0.341.0 — 2026-11-08 - The actor capabilities name the canvas routes (plan HO9): `GET /api/v1/me/canvases` and `GET /api/v1/me/canvases/:id` under `actor.task.read`, and `POST /api/v1/me/canvases/:id/events` under `actor.task.create`. The documents are `actor.canvas@1` (0.340.0); a button's label starts a normal turn as the person, and a checklist tick flips the item and is told to the next turn. No schema changed. ## 0.340.0 — 2026-11-08 - Add `actor.canvas@1` (ADR-059, plan HO9): an agent's live panel beside the conversation, as data. A canvas is a list of blocks (a heading, text, a list, a checklist, a table, a metric, a bar or line chart, a picture from openvibe.media, a link, a button) drawn by one renderer (OpenVibe.Shared `ov-canvas.js`) wherever it is shown. There is no HTML, style, script or frame; text is plain. A button only sends its label to the agent as the person's own message, and ticking a checklist item tells the agent which item changed: a canvas never acts by itself. `version` only grows, and the agent replaces the whole document. ## 0.339.0 — 2026-11-08 - The batches contract says what cancelling does. Only the prompts that never had a run are cancelled, and nothing is charged for them. A prompt that already has a run (the provider is working on it) goes on and settles on its own path: it keeps its status, the answer says so, and nobody is told it was not charged. The batch itself is cancelled only when nothing is left in flight. - The prompt's `reason` now names the three notes a customer can read: it expired (it was not charged), it was cancelled before it ran (it was not charged), or it was already sent to the provider (it finishes and is charged). ## 0.338.0 — 2026-11-08 - `actor.trigger@1` gains the goal kind (plan HO3): a trigger that keeps working toward a goal on a schedule. Additive: - `kind` may now be `goal`, and `state` may be `done` (a goal ended itself when its done-test passed); - `paused_reason` adds `done`, `budget` and `deadline` beside `person` and `failed`; - the optional `goal` object says the goal in the person's words, the `done_test` the agent answers yes or no each run, `runs_used` against `runs_max`, `spent_usd` against `budget_usd`, `deadline_at`, the last run's `not_yet` reason and its `verdict`; - the optional `answer_in_chat` (default true) says whether each run's answer is posted into the conversation that made the trigger, under its card, and `notify` (default false) whether the person is also told through OpenVibe's notifications. - Nothing that exists today changes shape: a schedule or event trigger without the new fields stays valid. - `actor.task-event@2` gains the `trigger-run` step (2.2.0): a routine's run posted into the conversation that made the trigger, under its card. Additive: `trigger_id` names the routine, `run` says which task ran and how it ended (`ok`, `failed`, `needs_you`), `text` is its answer or the one line saying why it did not finish, and `sources` are the addresses it read (at most 8). An event without them, and the `trigger` card step, are unchanged. ## 0.337.0 — 2026-11-07 - Add `actor.skill@1` and `actor.skill-listing@1` (ADR-058, plan HO2: the skills registry's skeleton). A skill is a document an agent loads to do one kind of job well: a slug, a name, a one-line description (the only part an agent reads before it loads it), a version, Markdown instructions, up to ten text resources, and `requires` (the approval kinds, connected apps and hosts it may ask for). It grants nothing: every step still goes through the agent's level and approvals. A v1 skill runs as a prompt only (no code, no binary, no install recipe). Its integrity hash shows the file was not edited; it is not a signature, and the author in a document is a claim. - The listing is the registry's record every site shows: the document, the publisher from the registry's own record, the scan (`pending`, `passed`, `flagged`, `failed`, with findings) and the curator's state (`active`, `stale`, `archived`, the last use and a near-duplicate). A passed skill installs in one tap, a flagged one after the person reads the findings, a failed one never; a stale or archived one is not offered. ## 0.336.0 — 2026-11-07 - The run's options carry the latency class the caller asks for: `interactive` (answer as soon as the provider can), `background` (minutes; the run is priced on the cheapest tier that can wait) or `batch` (hours; the run is queued and answered through a provider's own batch API). A batch run may also say `deadline_hours` (1 to 72): how long the answer may take. The class is the workflow's own (the route's `constraints.latency_class`): a caller may ask for a slower class and never for a faster one, and a faster ask is not refused — the run happens in the workflow's own class and says so. - The run's answer carries `latency_class` (the class the run really ran in), `service_tier` (`standard`, `flex`, `batch`, `offpeak`, `local`: the tier it was priced on — the one the provider served or the one the router chose) and `latency_clamped` (the class asked for and the class used, when the ask was clamped). - Add the batches contract: `ai.batch@1`, `ai.batch-item@1`, `ai.batch-create-request@1`, `ai.batch-create-result@1` and `ai.batch-read-result@1` for `/api/v1/batches` on OpenVibe.AI. A batch is a queue of many prompts that are answered later, at half the retail price; the batch says where it stands, and a prompt that reaches its deadline without cheap capacity expires and is never charged. - Add the grants `ai.batches.create` (open a batch, cancel one) and `ai.batches.read` (read the caller's own batches). A first-party service opens a batch on the network's own account with `ai.batches.create`; a person or a developer app pays with its own account. ## 0.335.0 — 2026-11-07 - Add `actor.voice@1`: an agent's voice as a file, and nothing else. The document carries the agent's name, a short bio, its instructions (the house voice it speaks in), its rules, its default approval level (`ask`, `auto-edits`, `approve-for-me`, `full` — the levels of `actor.agent@1`), the model it prefers and the connected apps it may use (ids only, never a key), an optional face (an avatar ref), an optional author and a `created_at`. It never carries memory, a connector's token or a file. - The document carries an **integrity** block, not a signature: `integrity: { alg: "sha256", hash }`, where the hash is the sha256 over the canonical JSON of the document. It shows the file was not edited after it was written. It says nothing about who wrote it: anyone can write a file that names an author and compute its hash, so the author in a document is a claim and the contract says so. Only the service that owns an agent may vouch for its author, in its own record. - The contract says exactly which fields the canonical JSON holds and in which order, and `created_at` is not one of them. The author may be absent: a document may name nobody. - Importing a voice makes a new agent at level `ask`, whatever the file says: a borrowed agent starts cautious and the person raises it themselves. Valid and invalid fixtures cover a full voice, a minimal one, a document carrying memory, one carrying a connector's token and one with a level that is not a level. - Additive only: a new contract and its fixtures; nothing that exists today changes shape. ## 0.334.0 — 2026-11-07 - The `chat` manifest names `openvibe.party`, the Watch Together host OpenVibe.Chat serves beside `openvibe.chat` (Party watch v1, plan R1). Chat's service manifest is what services check a party link against: a media party plays a Media ref or an https URL on a host a manifest names, so a party started from an OpenVibe page passes and a link to somebody else's server does not. The `www.` name only redirects, so it is not listed. ## 0.333.0 — 2026-11-07 - The `network` manifest names the two hosts Network serves since 2026-11-07: `openvibe.my` (the account hub's own home, Network#646) and `openvibe.link` (OpenVibe Link's home, Network#644). A service that builds its CORS list or its checks from manifest domains (Network's first-party origins, Chat's Bearer-only DM grant, the registry's nightly browser check, Space's reserved names) now includes them. The `www.` names only redirect, so they are not listed. ## 0.332.0 — 2026-11-07 - Add `common.control-lease@1`, the shared control lease (seven domains plan B3): the thing a lease controls, its holder (a `person`, an `ai` or `nobody`, with the holder's subject or null), when the holder got the controls, an optional absolute `expires_ms`, the subjects watching and a `seq` that increments on every holder change, expiry or take-back. A watcher-only change does not increment `seq`. The authority that owns the thing enforces the rules; Chat hosts a room's lease, names the thing `chat.room:<slug>` and accepts a control change only from the current holder, while take-back by the room's owner always wins; only the owner hands the remote to an `ai`, and a person holder who leaves keeps it for a minute. - Add `common.sync-clock@1`, the shared timeline (B4): the room, the media (a Media ref or an https URL; Chat accepts only a host an OpenVibe service manifest names, so Party plays OpenVibe media), the position at `at_ms`, the rate, whether it is paused, who set it and a `seq`. A player computes `position_ms + (authority now - at_ms) * rate` while it plays and `position_ms` while it is paused. Chat stamps `at_ms` and `seq` and stores the latest state in `room_sync_clocks` (the memory copy is the room's live state). - Valid and invalid fixtures cover a person, AI and free lease, a lease with a time limit, seq zero, a nobody with a subject, a duplicate watcher, a URL and a Media-ref clock, a clock at a rate and paused, an http URL, a bad room and a bad media object. ## 0.331.0 — 2026-11-09 - Add `actor.trigger@1`: a trigger made in an Actor conversation — its `trg_…` id, owner, agent, task, kind (`schedule` or `event`), the plain-language and machine schedule (a five-field cron in the person's timezone) or Watch event pattern and filter, the next run, the last run's result (`ok`, `failed`, `needs_you`), state, why it is paused, the consecutive failure count, the Watch subscription's own state and the ten-minute undo window. Owner-only: another person's trigger is the same 404 as one that does not exist. - `actor.task-event@2` gains an optional `trigger` and the `trigger` step: the card as it is created, edited, paused, resumed, undone or deleted in the conversation travels with the event, so a reader renders the live card without a second fetch. Additive: an event without one stays valid, and the task record is still the source of truth. - Additive only: a new contract and an optional field plus one new step value; nothing that exists today changes shape. ## 0.330.0 — 2026-11-07 - Add the `mail` service manifest: `openvibe.email` as the site **Email** (icon `mail`, the shared glyph font's E054), tagline "An address for every sign-up", the receive-only wording, legal profile `service`, and position 7, directly after Chat. It declares exposure `live`/`service`, port 5250, database `ov_mail`, the Stalwart mail server, `/api/health` and `/api/ready`, its shutdown and due-based recovery, an expand-only rollback, and the purge lease. - Add the two capabilities OpenVibe.Mail's API already checks: `mail.addresses.write` (make and retire a mask; make an agent inbox) and `mail.messages.read` (the person's addresses, message metadata and the newest burner code, never a body). Both are first-party, `owner`-constrained, and name their routes. Their request and result schemas are recorded in `compatibility/capability-schema-gaps.json` for now, like the config.manage capabilities added in 0.315.0. - The manifest lists no `eventsProduced` yet: the service publishes `mail.mask.created` and `mail.received`, but their payload contracts are not in this release. - Sites from the old position 7 up move one place (Services becomes 8, … Connect becomes 52) so Email sits beside Chat without breaking the unique-position rule. No contract changes shape; the only data change is the site order. ## 0.329.0 — 2026-11-07 - Add `mail.mask@1`: a per-site address on OpenVibe.Mail that delivers into its owner's OpenVibe mailbox and nowhere else. It carries the `msk_…` id, the owner's Network subject, the `<words>.<four characters>@<mask domain>` address, the person's label and purpose, whether it is enabled (a disabled mask leaves the SQL directory Stalwart reads, so it bounces) and when it was made. M1 is receive-only, so this is metadata: no body, no forwarding target. - Add `mail.burner@1`: a throwaway receive-only address with its `bnr_…` id, an owner (null for the ten-minute burner made without an account), the eight-character address on a rotating burner domain, the expiry, `first_message_only` and the creation time. The one-time read token a create call returns is deliberately not part of the resource. - Add `mail.agent-inbox@1`: an agent's address `<agent>.<user>@openvibe.email`, its `agi_…` id, the owner, the cleaned agent id and the creation time. The mail lands in the person's mailbox; one agent has one inbox. - Add `mail.message-summary@1`: the one shape a list API returns for a message — id, sender addresses, subject and received time. `additionalProperties` is false, so a `body`, `preview` or `html` field fails the contract rather than leaking into an API; the body is read only by the verification-code endpoint, in memory. - All four are new contracts, not changes to existing ones, and the minor is expand-only. Valid and invalid fixtures cover a disabled mask, a guessed address, an anonymous and a one-message burner, a bad owner or agent id, a non-address, and a summary that carries a body. ## 0.328.0 — 2026-11-07 - Add `actor.assignment@1`: one node of the tree of delegated work (plan §12) — id, root, parent, depth, origin (`person`, `routine`, `outside`), mode (`work`, `consult`, `notify`), reply (`resume`, `poll`, `none`), the agent and task it runs as, the node's level and effective level, the root's budget (limit, spent and remaining), state and timestamps. A node is one hand-off: a person giving Actor a task, a routine or Watch trigger starting a run, an agent handing work to another agent, or Actor starting a Codes or Space task. The effective level is the higher of the agent's own and the one inherited along a person's or routine's line; outside work never passes a level down. A cancel cascades down the tree, and a root's budget stops its children when spent. `GET /api/v1/assignments/:id` answers the node and its children and `POST /api/v1/assignments/:id/cancel` cancels the subtree; both are owner-only and another person gets 404. An existing task gets a root lazily. - Add the capabilities `actor.assignment.read` and `actor.assignment.cancel`, and name them in Actor's service manifest. They are the two assignment routes above. - `actor.task-event@2` gains an optional `assignment_id` on a step event, so the Dock's timeline can draw the work tree later. Additive: an event without one stays valid, and the task record is still the source of truth. ## 0.327.0 — 2026-11-07 - One Computer across Surf, Actor and the Dock (plan 2026-11-07: "Surf, Computer and Actor are one experience"): `platform.task@1` gains optional `computer` (`session_id`, `machine`, `where`, `cost_hint`, `driving`) — the browser or desktop session a running task opened, wherever it runs. `session_id` is OpenVibe.Run's `ses_…`; it is null only when the desk runs directly on the person's own machine through OpenVibe.Bot, where there is no Run session. `where` is `yours`, `home` or `network`, and `machine` is the person's own word for it. - `actor.task-event@2` and `agent.timeline-entry@1` carry the same `computer` object by `$ref`: the task's step in the Dock and the task record read the same fields, never a copy. - `run.session@1` gains optional `task_id` (the Actor task that asked for the session, so the session points back at the task) and `curtain` (the privacy screen: while it is up the session's still answers the curtain picture, never the screen). `run.session-create-request@1` gains optional `task_id`. - The `run.session.started` and `run.session.ended` event payloads carry the same optional `task_id` and `curtain`, so a session's own events stay valid with the new session shape. - Capabilities name the routes: `run.session.read` gains `GET /api/v1/sessions/{id}/still`, `run.session.control` gains `POST /api/v1/sessions/{id}/curtain`, `actor.task.read` gains `GET /api/v1/tasks/{id}/still`, and `actor.task.create` gains `POST /api/v1/tasks/{id}/takeover` and `…/giveback`. - Additive only: every new field is optional and nothing that exists today changes shape. ## 0.326.0 — 2026-11-07 - Add `network.menu-feeds@1`: the cards a person chose to show in the OpenVibe menu's "Choose your feeds", in their order, following them across every site. `on` holds up to four `<site>:<cardId>` ids (lower-case letters, digits and dashes) and `updated_at` says when the choice last changed. OpenVibe.Network keeps it at `GET` and `PUT /api/v1/me/menu-feeds`; a person who has never chosen gets `{ on: null }` from the GET. An empty array is a cleared choice. - Additive: a new contract only; nothing that exists today changes shape. ## 0.325.0 — 2026-11-07 - Add `media.vod@1` and `media.clip@1`: one recorded stream and one clip as OpenVibe.Media's API answers them. Both carry `watch_id`, the id openvibe.video addresses a watch page by (`openvibe.video/watch?v=<watch_id>`), and the shape around it that consumers already read — the numeric id (internal), the owning tenant, the app-local user ids, title and status, duration and size, the file basename, `playback_url`, thumbnail, storage provider, visibility, view counts and times; a clip also names the recording it was cut from, its window in that recording and whether an automation cut it. `watch_id` is **additive and optional** (11 characters of URL-safe base64 from 8 random bytes, one id space across VODs and clips, fixed for the row's life and never the numeric id), because a row written before Media's backfill may answer without one. - Valid and invalid fixtures cover a public recording with a watch id and an unlisted one before the backfill, a person-made clip and an AI clip, a watch id of the wrong length, one with a character base64url never has, one with a slash, and an unknown visibility. ## 0.324.0 — 2026-11-07 - Space's builder API moves under `/api/v1/builder/spaces`: `GET /api/v1/spaces` was the forum's old API (it lives on OpenVibe.Community since 2026-11-07, and Space still answers it with a redirect), so the API the person's own agent works through needs an address of its own. The capabilities name the new routes: `space.build.read` (`GET /api/v1/builder/spaces`, `…/:id`, `…/:id/files`, `…/:id/files/:path`, `…/:id/preview`), `space.build.create` (`POST /api/v1/builder/spaces`), `space.build.write` (`PUT /api/v1/builder/spaces/:id/files`) and `space.build.publish` (`POST /api/v1/builder/spaces/:id/publish`, and the person's answer at `POST /api/v1/agent/approvals/:id/decide`). - No contract shape changes: the bodies and the capability ids stay as they were. ## 0.323.0 — 2026-11-07 - Space's builder API says two more routes in its capabilities: `space.build.read` now names `GET /api/v1/spaces/:id/files/:path` (one file of one of the person's own spaces), and `space.build.publish` names `POST /api/v1/agent/approvals/:id/decide` (the person's answer to the agent's request to publish, which OpenVibe.Actor passes on with the person's own token). - No contract shape changes: `space.build.read` and `space.build.publish` keep the bodies they had. ## 0.322.0 — 2026-11-07 - Add `actor.agent@1`: one of a person's agents as a contact (plan §12 A-1) — id, owner, name, face, voice, model (`engine` runtime / own-keys / open-gpu / link, plus the model id), computer (null or a Computer kind and placement), connected app ids, OpenVibe Memory scope id (or null), approval level (`ask`, `auto-edits`, `approve-for-me`, `full`) and when it was added. Levels default to ask; money, passwords, deletions and sending to a new recipient always ask at every level, as a server-side floor. - Add `actor.agent-list@1`: a signed-in person's own agents (`GET /api/v1/agents`), Vibe first. Vibe is created on the first read and is not deletable. The public agent-system catalog keeps `actor.agent-list-result@2` and now has an explicit `GET /api/v1/agent-systems` for a signed-in caller. - Add `actor.level-change@1`: the body of `PUT /api/v1/agents/{id}/level`; optional `agent_id`, `thread_id` and `reason` are echoes, the path names the agent. - `actor.task-create-request@1` gains optional `agent_id` (the contact the task runs as; absent means Vibe) and `level` (the thread's own level). `platform.task@1` gains optional `agent_id`, `level`, `effective_level` and `agent_level`. The effective level is the higher of the agent's level and the person's own thread line; it never inherits from another person's or a service's work. - Name the contact routes in `actor.agent.manage` and the catalog and presets routes in `actor.agent.read`; add valid and invalid fixtures for the three new contracts. ## 0.321.0 — 2026-11-07 - Space's builder becomes a task of the person's one agent (plan 2026-10-22 §Z, stage Z1). Add the contracts of Space's builder API: `space.build-space@1` (one of a person's own spaces: its id, slug, name, description, whether it is published and where, and when it changed), `space.build-list@1` (`{ spaces }` or `{ space }`), `space.build-files@1` (the space's files, each a path and its whole content), `space.build-create@1` (the body of a request for a new space), `space.build-preview@1` (where the space can be seen now, its file paths, and whether it is live) and `space.build-publish@1` (`pending`, `published` or `declined`, with the request the person answers in the Dock). - Add the capabilities `space.build.read` (list and read the person's own spaces and their files), `space.build.create` (make a new space), `space.build.write` (write the files of one of them) and `space.build.publish` (ask to publish; the person answers before anything goes live). A first-party service names the person in `X-OV-Subject`, and only that person's own spaces are in reach. - Additive: nothing that exists today changes shape. ## 0.320.0 — 2026-11-07 - `frame.menu-card@1` items gain an optional `image`: `src` (https on the publishing site's own origin or an `openvibe.*` host, at most 400 characters), optional `w` and `h` (whole pixels, 16–1920) and an optional `alt` of at most 120 characters. Network keeps an image whose `src` passes the same host rule as the item's `url`, drops one from anywhere else, and passes it through in `GET /api/frame` so the Frame menu and Network's `/my` can draw a thumbnail. - Additive: every item without an image stays valid, so an older publisher file keeps passing and an older reader ignores the field. - Valid fixtures cover an item with a full image and one with only `src`; invalid fixtures cover `http`, a width under 16, an `alt` over 120, a missing `src` and an unknown image field. ## 0.319.0 — 2026-11-07 - The merged public site: OpenVibe.AI is no longer a site of its own. Its service manifest keeps the API, the run receipt and the operator console but drops the `site` block, so Network's Frame lists Exchange instead of AI. `manifests/products/ai.openvibe.services.json` is `kind: moved`, points at Exchange and keeps the API address, and Exchange's own site block now carries the merged tagline ("Every AI model, one account") and what it does. - No contract shapes change. ## 0.318.0 — 2026-11-07 - Add `network.presence@1`: one person's online status as another may see it — `online`, `away`, `dnd`, `invisible` or `offline`, an optional status line (80 characters) and one emoji, whether they are streaming on OpenVibe.Live (`live`), and when it changed. OpenVibe.Network owns it; the state is derived from a heartbeat (online under 2 minutes, offline after 3) and the last input (away after 10 minutes), and invisible or `show_status` off is seen as offline. - Add `network.presence-update-request@1`: the optional fields of `PUT /api/v1/me/presence` — the chosen state (`auto`, `dnd`, `invisible`), the status line and emoji, and the "Show my status" switch. - Add the event `network.presence.changed`, at most once per person per 30 seconds and only for a real change. Its payload is the public view: an invisible person is sent as offline with no status text or emoji, and the state list has no `invisible`. - Add the capabilities `network.presence.read` (OpenVibe.Network's `GET /internal/presence`, for Chat) and `chat.dm.peers.read` (OpenVibe.Chat's `POST /internal/chat/dm/peers`, for Network's "follows or has a DM" rule), and name them in the two service manifests. - Valid and invalid fixtures cover the presence, the update request and the event, so a bad subject, an unknown state, an over-long status or a second emoji fails the run. ## 0.317.0 — 2026-11-07 - Add `frame.menu-card@1`: one card a site publishes for the network's Frame menu (the menu's network cards). A card has a stable id and site, a title of at most 60 characters, a kind (`rail`, `rows` or `chips`), up to six items with a title of at most 80, an optional meta line of at most 120 and an https URL, an optional `more` link with a label of at most 24, the time it was built and its ttl. A URL must be https on the publishing site's own origin or an `openvibe.*` host; the text is plain words and Network strips markup and drops unknown fields. - Add `frame.menu-cards@1`: the public file each open site serves at `/.well-known/openvibe-menu.json` — an array of at most three cards from that site. An empty array is valid (the site has no card right now); Live serves its rail with empty items when nobody is live, and Network then skips it. - Valid and invalid fixtures cover a live rail with and without items, a Games rows card, a three-card file, an empty file, a title over 60, seven items, an unknown kind, an `http` URL, an unknown field, a ttl under 30, four cards and a card with a bad timestamp. ## 0.316.0 — 2026-11-07 - Add `agent.timeline-entry@1`: one entry of the agent's timeline (design 2026-11-07 §2) — one step of the agent's work, in one mode, for one person. It carries the mode, the product, the step's own id, a plain title, when it started and ended, its status (running, waiting, needs_you, done, failed, stopped), the placement (where it ran and why), the cost, a link to the step's page, the one-line reason when it failed, the transition sentence when the mode changed, and the audience (private is Only me, the default). It never carries a prompt, a diff or a page. - Add `common.agent-step@1` and the three events `agent.step.started`, `agent.step.updated` and `agent.step.ended`: the one shape every mode publishes when the agent does something (Chat, Codes, Run, Watch, Live, Surf), with the person's subject on the envelope. The payload is the step's shape only — a title, an outcome, a link, a cost — and never the step's content: a prompt, a diff, a page, a message or a file is refused by the schema. The `agent.*` namespace belongs to the network (like `provider.*`), so any first-party mode may publish into it; OpenVibe.Actor registers the family in Contracts and keeps the one timeline behind the Dock. - Add `agent.timeline-list-result@1`: what the Dock's Agent panel reads — the step happening now, what needs the person (oldest first), the entries themselves (newest first), the next cursor, and the per-mode counts so a filter never hides without a count. - Add `actor.timeline.read`: `GET /api/v1/agent/timeline`, the person's own timeline with the cursor and the mode and needs-you filters. Only their own entries; another person's entry is a 404. - Valid and invalid fixtures cover a step that starts, waits and ends, a timeline entry and a list, a payload with a prompt or a diff in it, an unknown status, a mode that is not one of the seven, and an entry of another person. ## 0.315.0 — 2026-11-07 - The owner's Setup tab reaches seven more services: `<service>.config.manage` for `chat`, `connect`, `deals`, `work`, `sources`, `design` and `domains` is now **active** and listed in each service's manifest, because each mounts openvibe-shared/config's routes on `/internal/config` (`GET /internal/config`, `GET|POST /internal/config/:namespace`, `PUT|DELETE /internal/config/:namespace/secrets/:key`, `POST /internal/config/:namespace/test/:key`). Only Network's admin proxy, acting for the owner, holds them and they are never grantable to apps. `media.config.manage` stays planned until Media mounts the routes. - Add `blog.review.ai_editor`, `news.review.ai_editor` and `wiki.review.ai_editor`: the editorial queue's AI editor decision (approved or rejected; approved or needs changes on Wiki) recorded by the first-party service principal acting as itself (a `svc:` principal with no `X-OV-Subject`) through Blog's `POST /api/v1/posts/:id/reviews`, News's `POST /api/v1/stories/:id/reviews` and `POST /api/v1/briefs/:id/reviews`, and Wiki's `POST /api/v1/pages/:id/revisions/:n/review`. An approval lets AI-written text publish and index and the page says "Checked by OpenVibe's AI editor" with its date, never "Reviewed by a person"; each is granted only to the orchestrator's service principal. Their requests and results are a recorded schema gap (`compatibility/capability-schema-gaps.json`). - `generated/` refreshed: Connect and Design gain an OpenAPI document now that each has an active capability, and Blog, Chat, Deals, Domains, News, Sources, Wiki and Work gain the new operations. ## 0.314.0 — 2026-11-07 - Add the capability `live.agent_sandbox.register` (Agent Streams S6): the chat plays sandbox worker's check-in on OpenVibe.Live, `POST /api/streams/agent/:id/plays/worker`, owned by Live and internal. Only a first-party service token whose principal holds it may register a worker, and the token names the person it works for in `X-OV-Subject`, who must own the stream. The owner's own session, a desk ticket (`wpt_`) and a person token are refused: an owner who could register a worker would declare their own unisolated machine a sandbox. The worker reports the isolation facts Live requires (no host network, no credentials, no profile, egress through the proxy, an ephemeral workspace) and Live derives health only from a check-in within the last minute; Live stores no endpoint and never calls the worker. Name the capability in Live's service manifest. - Add `live.agent-sandbox-worker-request@1`: the worker's check-in body, which names the five isolation facts Live reads exactly (`host_network` false, `credentials` false, `profile` false, `egress_proxy` true, `workspace` `ephemeral`) and may carry the worker's own checks, limits, versions and counts. A worker that cannot say each of the five is not registered. Add `live.agent-sandbox-worker-result@1`: the worker's state for the stream's owner, with the reason when there is no healthy one, the last check-in and the minute a check-in counts for. Live never returns an endpoint: there is none. - Valid and invalid fixtures cover a full worker check-in and an isolation-only one, a worker with the host's network and one that cannot say there is no profile, and a healthy worker, a stale one, a missing stale window and an unknown state code. ## 0.312.0 — 2026-11-07 - Add the Codes skills-hub records (the Compute plan §5, E6). `codes.skill@1` is one skill, rule, MCP server, command or hook: its owner, kind, stable slug, title, summary, the four sharing audiences (`private`, `people`, `link`, `public`), the harnesses it is written for, the newest version and that version's body. A `private`, `people` or `link` record is never in the gallery; only `public` is. The body is untrusted Markdown and the page renders it through the markup gate. - Add `codes.skill-version@1`: one immutable version of a record, keyed by `skill_id` and a growing `version`, with the body, summary, tools, audience, author and the note that says why it was written. A rollback copies the chosen version into a new one rather than rewriting history, so a Link that synced version 3 can still name it. - Valid and invalid fixtures cover a public skill, a private rule, a people-shared MCP record, versions 1 and 4, and the bad ids, kinds, audiences, tools, bodies and authors. ## 0.311.0 — 2026-11-07 - Add `<service>.config.manage` for the eight services that hold an owner input and that Network's admin proxy now reaches for the owner's Setup tab (OWNER-SETUP-AND-ADMIN-CLARITY-DESIGN, 2026-11-07): `chat`, `connect`, `deals`, `work`, `sources`, `design`, `domains` and `media`. Each is internal, held only by Network's admin proxy acting for the owner, never grantable to apps, and **planned** until its service mounts openvibe-shared/config's routes on `/internal/config`; the service then turns it active and lists it in its manifest (Deals' own test pins that list, so listing it early would break its pin bump). Planned capabilities verify like active ones, so Network's grant works the day a service mounts the routes. - Every `<service>.config.manage` (the nine active and the eight planned) names `POST /internal/config/:namespace/test/:key` among its routes: openvibe-shared 4.125.0's test hook, which tries a stored value with the provider on the server and answers `{ ok, message }` in one line that never carries a secret. - `common.config-namespace@1` (description only): the summary's schema may carry the annotations the Setup tab and the clear admin panel read, `x-owner-input`, `x-required-for`, `x-enum-labels`, `x-advanced` and `x-example`, beside the D11 ones (and `x-unit` names `seconds`). No shape changes. ## 0.310.0 — 2026-11-07 - Apps ask for consent per purpose at sign-in (the consent plan's C4). An app adds one scope `consent:<purpose>` per purpose to /oauth/authorize, beside its other scopes. It may ask for every purpose of the consent family except the retired `ai.looks`: `ai.train`, `data.public`, `face.recognize` and `app.data`. Any other `consent:` scope is refused with `invalid_scope`. The consent screen lists each purpose in plain words, nothing ticked, and the person can sign in with every purpose refused. A refused purpose is not asked again by that app for 30 days, unless the person asks for it again on their account page. - Add `network.consent-claim@1`: the `consent` claim of the tokens Network issues from that sign-in (a first-party user token, a Dock token or an app token). It holds exactly the purposes the sign-in asked for that the person granted to that app and that are active when the token is issued, `[]` when none. It never holds a refused purpose or another app's grant, and the app learns only "not granted". The claim is absent when no consent scope was asked. Every new token works it out again, so a purpose turned off is gone from the next one. - `identity.service-token-claims@1` gains the optional `consent` claim (additive): an app token from the authorization-code flow carries it when the sign-in asked for consent scopes. - Add `network.me-consent-apps-result@1`: "Apps you allowed" on the account page (`GET /api/v1/me/consent/apps`), listing each app's granted purposes and the ones refused in the last 30 days (`quiet`, with the day it ends), with the words the app consent screen shows. `network.consent.manage` names that route and `DELETE /api/v1/me/consent/:purpose/:client/refusal`, which ends a refusal's quiet days so the app's next sign-in asks again. - `network.consent@1`: an app's grant is one record per purpose, with the app as `client` and `source` `oauth`. `client` is no longer only for `app.data`, and `options.purposes` is not written any more (kept so older records validate). `network.consent-check-request@1`'s `client` may name the app for any purpose: one app's grant never answers for OpenVibe itself or for another app. Descriptions only: no shape changes. - Valid and invalid fixtures cover a user token with one purpose, an app token with none and with all four, a retired, unknown, duplicated or missing claim, a claim that is not a list, an app's grant record, the account page with two apps and with none, a bad client id, a retired purpose, an extra key and a quiet refusal with no end. ## 0.309.0 — 2026-11-07 - Add `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, `llama-server --rpc` on the head). OpenVibe.Network holds the record: the owner proposes it on /machines with two to eight of their own machines (the default head has the most free VRAM), and each machine reads it with its own node token and approves it on itself. A member's `certificate` is null until it approves; then it is that machine's self-signed Ed25519 public certificate, which the other members pin for the pool's private link. The private key stays on the machine: a certificate slot holding anything with `PRIVATE KEY` in it is refused, and so is any field the schema does not name. A member's `address` is a private RFC 1918 address and prefix (never 0.0.0.0, loopback or a public address) and its `public_ip` is the address Network saw it from. The rules a schema cannot say are written in the description and checked by Network and Node: the head is a member, every member is the owner's today, all share one public address and one private subnet, the head's direct link probe to each passes, every key is its own, and any change of membership, owner, head or key revokes the pool so every machine approves a new one. - Add `ai.offer@1`: one community AI offer as Bot reports it to Network and AI reads it back (the fields Network already stores: machine, owner, models, hours, concurrency, credit price, audience, readiness and health), with the new optional `pool`. - Add `ai.offer-pool@1`, the `pool` part of the AI offer: `{ machines, head }`, two to eight machine ids and the head, which is the offer's own machine. Network adds it from its approved pool record, never from what a machine reports, and leaves a member's own offer out while it is in a pool. It names machines only: no address, key or certificate reaches AI. One price, audience, hours, idle-only and verification apply to the whole pool; AI verifies a pool's answers as it would one machine's, keys that verification on the exact membership, keeps a new or failed pool to the free tier only, and labels it "Community pool (N machines)". - `network.node.self.manage` names the five pool routes a machine calls with its own token (announce, read, approve, renew readiness, leave) in `implementedBy`. - Valid and invalid fixtures cover a pool waiting for approvals, an approved pool of three, a single-machine offer, a pool's offer and one still waiting, a group offer, a missing head or owner, a one-machine pool, a repeated member, a private key in the certificate slot, a public, loopback or wildcard address, a port under 1024, and an address, certificate or key smuggled into a field the schema does not name. ## 0.308.0 — 2026-11-07 - Add `ai.memory-export@1` (Memory M5): the one portable memory file. It carries `format: openvibe.memory-export@1`, `version: 1`, `exported_at` and every item's text, kind, scope, source, confidence and source reference. It deliberately has no ids, owners, merge links or conflict pair, so a file can be imported into another account without carrying anyone's identity, and a profile is derived from the items and rebuilt, never imported. OpenVibe.AI's `GET /api/v1/memory/export` writes it and `POST /api/v1/memory/import` accepts the same document; both redact again. - Valid and invalid fixtures cover an export with items and an empty one, a wrong format/version and a scope that is an absolute path instead of `user` or `project:<slug>`. ## 0.305.0 — 2026-11-07 - Give the id kinds the S7 work needs a prefix: `newId('agent_stream_schedule')` is `scd_…` (a scheduled public agent stream) and `newId('party')` is `pty_…` (a watch party's id when a party is not named by its room slug). - Add `live.agent-stream-schedule@1` (Agent Streams S7): an owner's schedule for a public agent stream — a time, a task template and the mode. Live starts it when the owner's desk is online; when the desk is not, the record says `missed` with `desk_offline` ("didn't start: the desk was offline") and nothing runs. A `chatplays` schedule keeps its time but waits for the owner's Go (`waits_for_go`), the S6 rule. The record carries the stream it started, its reminder count (the people who opted in, never who they are) and the page where it shows 24 hours before its time. - Add `chat.party@1` (Agent Streams S7): a watch party on a public agent stream or its VOD, a Chat room of kind `party`. On a VOD the position follows the host's; on a live stream it follows the live edge. A party is private by default, joining is by its link, and a listed party reaches the stream's page only as a count ("3 parties watching") — the count never carries names. - Extend `live.agent_stream.publish` with the schedule routes and the desk heartbeat: `POST|GET /api/streams/agent/schedules`, `DELETE /api/streams/agent/schedules/:id`, `POST /api/streams/agent/schedules/:id/remind`, `POST /api/streams/agent/schedules/:id/go` and `POST /api/streams/agent/desk/heartbeat`. - Valid and invalid fixtures cover a scheduled stream, a missed one with the desk offline, a chatplays schedule that waits for Go, a missing task and an unknown state; a private VOD party, a live party with a link, a party made public and a party with a room id that cannot exist. ## 0.304.0 — 2026-11-06 - Give the id kinds the Agent Streams work needs a prefix: `newId('agent_stream')` is `ags_…` (an agent stream's id), `newId('play_request')` is `req_…` (a chat plays request) and `newId('sandbox_session')` is `sbs_…` (the sandbox desk). Without them a service that had to name one of these ids had to build the ULID itself, and `newId('ags')` threw. - Add `chat.play-request@1` (Agent Streams S6): one viewer's request for the chat plays desk to build or do something. A request is written with `!do <request>` (at most 200 characters) in an agent stream's room, `agent:<ags_id>`; one open request per person at a time, and an account younger than a day cannot ask. The request carries its votes, its state (`queued`, `running`, `done`, `failed`, `skipped`, `removed` or `refused`), whether it is pinned, and the one-line reason it was refused (with the rule that refused it) when it was. A refused request carries no result; a finished one carries the outcome, the line the overlay and the voice said, and the scratch site the run left. - Add `codes.sandbox-session@1`: the sandbox desk a chat plays session runs on. The desk is started with `--shared` — no profile, no keys, no person's files, public egress only and a fresh workspace, wiped at the end — and the session carries the owner's time box (5 to 30 minutes), the step budget, the cap (money and minutes), what it has spent, whether the owner armed it with Go, and the throwaway scratch site with the 24 hours it lives. `egress_public_only` is always true: no private address is reachable. - `live.agent-stream-control@1` gains the control `wipe` (additive): Wipe now stops the run and resets the sandbox. The other four controls (pause, curtain, mute, end) keep their shape. - `live.agent-stream@1` gains the mode `chatplays` (additive; the enum already carried it): a chat plays stream is public or unlisted, and it works the room's queue. - Add the capabilities `chat.play_queue.read` and `chat.play_queue.write` for Codes, the task's service, acting for the stream's owner (a node token `on_behalf_of`), and name both in Chat's service manifest. Chat chooses what the queue holds: a request enters it only after the house text filters and the safety check, one open request per person at a time, and the people and the votes in it belong to the room. - Valid and invalid fixtures cover the queued, finished and refused requests, a request that is too long, a refusal without a reason, an unknown state, the armed and running sandbox sessions, a session with no cap, a time box outside 5 to 30 minutes, a sandbox that is not public-only, and the wipe control, so a missing field or a bad value fails the run. ## 0.303.0 — 2026-11-06 - Add `chat.agent-digest@1` (Agent Streams S5): what a Co-pilot room said to an agent between two of its steps. The agent never reads the raw room: it asks Chat for a digest and gets at most ten items and 1,500 characters. Each item names who (the display name and the handle), what (cut to 200 characters), its kind (`question`, `suggestion` or `reaction`) and its upvotes; a repeated line is one item with the number behind it. Chat chooses what goes in (repeats grouped, greetings and spam dropped, the most-upvoted questions and suggestions first) and who is left out (a person who is timed out or banned, whose account is younger than a day, or who set 'Agents don't read my messages'). A link is kept as its domain only; the code and the commands stay as text. - Add `chat.agent-poll@1`: the poll an agent asks a Co-pilot room to run, and the result it gets back — the question and the two to four options the agent wrote, the window it is open for, the state (`open`, `closed` or `vetoed`) and the counts. An open poll carries no result, a closed one carries its counts and its winner, a vetoed one names who refused it. - Add `live.agent-stream-control@1`: one of the four controls the owner and the channel's moderators have over a live agent stream — `pause`, `curtain`, `mute` or `end` — with who pressed it (the handle and the role: owner or moderator) and when, and whose pause is in force. - Add the capabilities `chat.agent_digest.read` and `chat.agent_poll.write` (first-party), and name both in Chat's service manifest. The caller is Codes or Actor, the task's service, acting for the stream's owner (a node token `on_behalf_of`), so a digest is the owner's room and nobody else's. - `chat.preferences` is v2: it gains `agents_read_messages` ('Agents don't read my messages'). A v1 record upgrades with it true — joining a Co-pilot room and writing in it is the choice, and the person can take it back. - `live.agent_stream.publish` now names the controls (`POST /api/streams/agent/:id/control`, `GET /api/streams/agent/:id/controls`) and the step credit: a step may name the suggestion it took. - Additive: nothing that exists changes. Fixtures cover a digest over the ten-item cap, an unknown kind, a room that is not an agent room, a poll with one option and with five, an open poll carrying a result, an unknown control, a control with nobody behind it and a role that is neither owner nor moderator. ## 0.302.0 — 2026-11-06 - Add the capabilities `media.avatar.adopt` and `media.avatar.release` (internal), and name both in Media's service manifest. After the 2026-11-06 incident (eleven avatars were soft-deleted with the public screenshot copies), Media records which avatar object each profile slot points at, and its delete refuses an object a working profile still shows. A product (Network for `users.avatar_url`, Live for its own copy and the channel banner) records a slot with `POST /internal/avatar-adopt` and drops it with `POST /internal/avatar-release`. - Add `media.avatar-adopt-request@1` / `media.avatar-adopt-result@1` (the slot: `object_id` `med_…`, `product`, `ref`, the optional `subject`; the answer is the active reference) and `media.avatar-release-request@1` / `media.avatar-release-result@1` (a slot, an object or a person; the answer counts the released references, 0 when there were none). - Additive: nothing that exists changes. Fixtures cover a missing ref, an object id that is not `med_…`, a release that names nothing, a product without its ref and a negative count. ## 0.301.0 — 2026-11-06 - Add `live.agent-stream-state@2` (Agent Streams S4): the state the desk polls gains the voice (`mode` watch or narrated, the `voice`, the `personality` calm, hype, dry or roast, `muted`, `paused` with its reason), the newest `lines` to speak, and `captions_url` (the stream's WebVTT). `@1` is unchanged. - Add `chat.tts-speak-request@1` and `chat.tts-speak-result@1`: the body and answer of `POST /api/agent-voice/speak` on OpenVibe.Chat (one line of at most 220 characters, an optional voice or the person's own; the audio, its MIME type and rate). - Add the capability `chat.tts.speak`: a machine (a node token acting for its owner, `on_behalf_of`) asks Chat to speak a line in that person's chosen voice, never another person's. Chat's manifest names it. - Additive: nothing that exists changes. Fixtures cover a missing text, a line over 220 characters, a missing audio and a wrong MIME type. ## 0.300.0 — 2026-11-06 - Add `report.session@1`: the report of one agent session, whatever ran it — a Codes task, an Actor task, a desk session or a session on a person's own machine (the Agent Streams plan, S3). It carries the id (`rps_<ULID>`), the owner (`usr_…`), the source (`codes`, `actor`, `desk` or `node`), the task (title ≤ 200 characters, the task's ref and its link back), the start and end, the worked seconds, the outcome (`done`, `stopped` or `failed`, with `stopped_because` in plain words when it did not finish), the summary (≤ 600 characters), the timeline (the steps, the same shape as `live.agent-stream-step@1`), the outputs (`pr`, `file`, `page`, `deploy`, `clip` or `vod`), the proof (the tests, the screenshots' media ids and the review's verdict), the cost and the models, up to five highlights, the agent stream it streamed on and the visibility (`private`, `unlisted` or `public`). The raw transcript, secrets and code are never in a report: the source service keeps the transcript for the owner, the house redactor runs over every text field before anything is stored, and a pull request is a link (its content shows only when the repository is public). - Add `report.session-write-request@1` and `report.session-write-result@1`: the body of `POST /internal/session-reports` on OpenVibe.Report and what it answers. The route is idempotent per (source, task ref): the same session's task always answers the same report. The report's id is Report's own; the body never carries one. The write request's `stream_only` says a stream-only report (Live's, when no tool finished the session). - Add the capability `report.session.write` (first-party) and name it in Report's service manifest. The caller is a first-party service — Codes, Actor or Live — with a service token that holds it for the audience `openvibe.report`. - Valid and invalid fixtures cover the report, the write request and the write result, so a missing owner, an id that is not `rps_…`, an outcome outside the list or a URL that is not a Report page fails the run. ## 0.299.0 — 2026-11-06 - Add `live.agent-stream-step@1`: one step of the work an agent is doing on an agent stream (the Agent Streams plan, S2). It carries `at` (when the step began), `title` (at most 120 characters), `status` (`started`, `passed`, `failed` or `skipped`), the optional `detail` (at most 500 characters), the optional `tests` (`passed` and `failed`) and the optional `cost_usd`. The step is plain text: the house text filters have already run over it before Live stores it. - Add `live.agent-stream-step-request@1`: the body of `POST /api/streams/agent/:id/steps` on OpenVibe.Live, which answers 201 with the step it stored. The publisher posts with its publish ticket, and Codes and Actor post through their service tokens with `live.agent_stream.publish` for the person; the capability is the one S1 named, so no new capability is introduced. - Additive: nothing that exists changes. Valid and invalid fixtures cover both, so a missing title, a title over 120 characters, a detail over 500 characters or a status outside the list fails the run. ## 0.298.0 — 2026-11-06 - Add `live.agent-stream@1`: an agent's own stream on OpenVibe.Live (the Agent Streams plan, S1). It is a `managed_streams` row of kind `agent` with an `ags_<ULID>` id, no stream key and no normal slot; it carries the owner, the channel, the task it is doing (`codes`, `actor`, `desk` or `node`), the visibility (`private`, `unlisted`, `public`), the mode (`watch` in S1), the state, the curtain, the viewers, the worked seconds, the last frame, the VOD and the page. The card stays after the stream ends. - Add the four request/result schemas: `live.agent-stream-create-request@1` (title, task, visibility, the optional `allow_sites`), `live.agent-stream-publish@1` (`whip_url`, the one-time publish ticket `wpt_…`, `expires_at`), `live.agent-stream-state@1` (viewers, `encode`, the curtain and `ends_at`, what the desk polls) and `live.agent-stream-curtain-request@1` (`up` and the reason). - Add the event `live.agent_stream.changed` (`started`, `ended`, `visibility`, `curtain`, never the reason) and the capability `live.agent_stream.publish`, and name both in Live's service manifest. The stream's contract ids keep the design's hyphen (`live.agent-stream@1`); the capability and the event type carry an underscore because `identity.service-token-claims@1`'s `cap` pattern and `events.event-envelope@1`'s `event_type` pattern allow letters, digits and underscores only. The capability's description records the design's `live.agent-stream.publish` name and why it is written `live.agent_stream.publish`. `identity.service-token-claims@1`'s `on_behalf_of` description now says a node token names its owner on the audience where the machine acts for them (Live's), so a receiver knows where to read the stream's owner. - Valid and invalid fixtures cover the stream, the create request, the publish part, the state, the curtain request and the event payload, so a missing field, a bad id or a reason outside the list fails the run. ## 0.297.0 — 2026-11-06 - Add `identity.subject-assertion-claims@1`: the claims of a short-lived ES256 assertion signed by OpenVibe.Network that says a person allowed one service to act for them on another service — `sub` (the person), `aud` (the service it is for), `act` (the service it was minted for), optionally `for` (the services that may carry it), `jti`, `iat`, `exp`. Network mints one only from the person's own access token (`POST /internal/subject-assertions`), so a service that merely knows a person's id cannot say the person allowed anything; the key is published in Network's JWKS (`kid ov-network-assertion-1`). The money routes of Billing and Tips check it against the person they act for, and refuse the old "the service vouches for its person" way once `BILLING_REQUIRE_SUBJECT_ASSERTION` / `TIPS_REQUIRE_SUBJECT_ASSERTION` is on. Only a service token Network issued for its own API (`openvibe.network`) mints one. See the ADR-003 amendment of 2026-11-06. ## 0.296.0 — 2026-11-06 - Sharing learns locked items. `share.update-request@1` gains `envelope`: a `locker.envelope@1`, base64, the content re-sealed for the people who stay, sent with the members that remain when someone is removed from a people-locked item. The service stores it before it drops the member; it never opens it. - `share.transfer-request@1` gains `key_slot` (that recipient's people slot), required for a people-locked item and checked against the recipient's current key from the key directory. - `share.member@1` gains `key_fingerprint` and `fingerprint_changed` (owner-only): the member's current eight-word fingerprint and whether it is still the key their slot was made for, so the share sheet can warn and re-wrap. - Additive: every new field is optional and old documents stay valid. Fixtures cover the locked member, the locked member input, the re-seal request and a locked transfer, and a malformed slot is refused. ## 0.295.0 — 2026-11-06 - Add `sources.category@1`: one category of the sources registry and how long OpenVibe.Sources keeps its items (roadmap §3 F). The `retention` field is either `{ days, reason }` — the window an item lives after it was last current (its last fetch while live, its removal once removed) — or `"keep"`, for reference data that is never pruned by age (Sources keeps the newest row per source key and prunes only the rows a newer value replaced). - The categories are the ones `sources.source@1` and `sources.item@1` already use; the three lists are compared in `test/run.js`, so a category added to one of them without the others fails the run. No existing schema changes: `sources.source@1`'s `category` and `sources.item@1`'s `category` keep their shape, so a consumer that reads them is unaffected. - Valid and invalid fixtures cover a windowed category (`news`), a kept category (`housing`), an unknown category, a missing rule and a zero-day window. ## 0.294.0 — 2026-11-06 - Add the unsafe-image review capabilities: `vision.image.review` (Media, Community and Live sort an image or AI text into fine, sex acts or sexual content involving a minor), `vision.review.read` and `vision.review.decide` (the staff queue and an operator's decision), `media.object.review.view` (Vision's 60-second logged evidence view of a hidden or quarantined object) and `media.object.review.decide` (Vision applies an operator's overruling category and releases Media's evidence hold). The Vision and Media service manifests name them. ## 0.292.0 — 2026-11-06 - Add `locker.envelope@1`: the header of a sealed item — the suite (AES-256-GCM), the chunk size (1 MiB), the padded size, the counter nonce base, the lock kind and one key slot per way in. The content key is never in the header; it is wrapped in a people slot (ECDH P-256 + HKDF-SHA-256, bound to the recipient's key by its RFC 7638 thumbprint), a password slot (PBKDF2-SHA-256, 600000 iterations, the parameters in the header so they can be raised, and bounded above at 10000000 so a hostile header cannot freeze a reader) or a link slot (empty: the key travels in the link's fragment). - The padded size is padded with Padmé (PoPETs 2019): at most about twelve per cent over the real size, never below 256 bytes, and it leaks O(log log n) bits. Every chunk but the last is a whole chunk; the last holds the rest. - Add the key directory contracts: `locker.public-key@1`, `locker.public-keys@1`, `locker.keys-put-request@1`, `locker.key-backup@1` and `locker.key-change@1`, with the eight-word fingerprint (sixty-four bits, in two groups of four) and the signature that proves a person owns their signing key. - `share@1` and `share.update-request@1` gain `lock` (`none`, `people`, `password`, `link`), and a share member may carry that member's key slot, so a member and its slot are saved together. Additive: the fields are optional and old documents stay valid. - Valid and invalid fixtures cover every new contract, and the new fields of the sharing contracts. The format is written down in [docs/locker.md](docs/locker.md). ## 0.291.0 — 2026-11-05 - Add `share@1`: resource refs, audiences, members and member inputs, transfers, access requests, uniform API requests and responses. Transfers are recipient-bound, last seven days, and default to removing the former owner's membership. - The uniform response names people: `share.person@1` on the owner, members, a pending transfer's `to` and each pending request; plus `share.you@1`, pending `requests` (`share.access-request-view@1`), the pending transfer view, and the service's `describe()` fields (`title`, `link`, `roles`, `audiences`, `transferable`). Add `share.people-lookup@1` for the exact-name people endpoint. - PUT accepts `notify` (default true); access requests can be `dismissed`; a transfer request accepts `{ name }` or `{ subject }`. - Add the five `share.*` event payloads with strict ids-only fields; Network owns the model, adopting services produce events using their own source. Valid and invalid fixtures cover every new contract. No existing schema changes. ## 0.290.0 — 2026-11-05 - **A comma-separated body, and an identity from several columns** (OpenVibe.Food's USDA ERS fruit and vegetable prices). An api endpoint of `sources.source@1` and `sources.source-write-request@1` may say `"format": "csv"`: a comma-separated file with RFC 4180 quoting (a UTF-8 BOM, a quoted field holding commas, doubled quotes and line breaks), whose first line names the columns, read as an array of records keyed by those names (`items_path` is ""). `"identity_fields"` names two to eight columns whose values, joined in order with " | ", are the record's identity, for a table where no single column is unique (USDA ERS prices each product in several forms); it requires `items_shape` array and cannot be given with `fields.identity`. Additive: the existing formats, `duplicates` and `fields.identity` are unchanged, and an older reader ignores the new field. Valid fixture: `fixtures/sources.source/valid/api-csv-identity-fields.json`. ## 0.289.1 — 2026-11-04 - **`billing.credit-hold@1` answers a personal hold.** `project_id` is no longer required and its type is `[string, null]`, so the hold Billing sends after a personal `billing.credit-hold-create-request@1` (no `project_id`) validates. A project hold still carries its `prj_` id and an app subject still always names one. `compatibility/corrections.json` records the change against v0.289.0 because the compat gate treats the widened type as a retype; the string-only rule never described a personal hold's answer, since that case could not be produced validly. Valid fixture: `fixtures/billing.credit-hold/valid/personal-hold.json`. ## 0.289.0 — 2026-11-04 - **Balance and Vibes Pay (AI Exchange D12, MONEY.md build order step 1).** - **Vibes Pay schemas:** `billing.vibes-charge@1` (id `vch_<ULID>`, app, project_id, `amount` in whole Vibes, a 1–140 description, the app's reference, status pending|paid|refunded|expired|cancelled, pay_url, return_url, the timestamps and sandbox), `billing.vibes-charge-create-request@1` (`amount`, `description`, `reference` — the app's idempotency key — and an optional `return_url`) and `billing.vibes-charge-refund-request@1` (an optional `reason`; a refund is always for the whole charge). - **Vibes Pay events:** `billing.vibes.charge.paid@1` and `billing.vibes.charge.refunded@1` are also the signed webhook bodies (HMAC-SHA256, retried with backoff): the charge fields plus an opaque `payer_ref` that is stable per app, and deliberately no person id and no email. Billing's manifest lists both in `eventsProduced`. - **Balance:** `billing.balance@1` gains the D12 Balance branch — `owner` (user or project), `balance_nano_usd`, `held_nano_usd`, `available_nano_usd` and an optional `sandbox`, with money as integer nano-USD decimal strings (D1, never a float) — answered by `GET /api/me/balance`. The contract id is permanent, so the Vibes wallet body that `billing.balance.read` already answered stays as the second branch; both branches are closed with `additionalProperties: false`, and new code sends the Balance branch. - **Balance requests:** `billing.balance-transfer-request@1` (`to_project_id`, positive `amount_nano_usd`, `idempotency_key`) for moving a person's Balance to a project's, and `billing.vibes-purchase-from-balance-request@1` (`vibes` as a whole positive integer, `idempotency_key`) for Get Vibes with Balance. - **Vibes read:** `billing.vibes-wallet@1` is the person's own Vibes — credit, creator payable and bits_per_usd, never a Balance — answered by `GET /api/v1/vibes/me`. - **Capabilities:** `billing.vibes.charge` (public, grantable to apps, a sandbox app included, which charges sandbox Vibes only) implemented by `POST /api/v1/vibes/charges`, `GET /api/v1/vibes/charges/:id` and `POST /api/v1/vibes/charges/:id/refund`; `billing.vibes.read` (public, a person-delegated read of their own Vibes for an app they approved) implemented by `GET /api/v1/vibes/me`. - **Personal holds:** `billing.credit-hold-create-request@1` no longer requires `project_id`; without it the hold is personal and spends the subject's own Balance. An `app` subject must still name its project (`if`/`then`), so the accepted set only grows and the compat gate stays green. - Valid and invalid fixtures cover both Balance branches, whole-number Vibes, D1 decimal-string money, a personal hold, an app subject without a project, a partial-refund body, and webhook payloads with a person id or an email-shaped payer reference. ## 0.288.0 — 2026-11-04 - **Configuration contracts (AI Exchange D11, S1), built on openvibe-shared/config: `common.config-namespace@1`, `common.config-change@1` and `common.config-secret-put@1`** (internal). - `common.config-namespace@1` is the shape `openvibe-shared/config`'s `summary()` returns (`service`, `namespace`, `revision`, `values`, `classification`, `active`, `last_known_good`), plus `sealed`, `pins` and `schema`: whether a KEK seals secret values, the keys pinned by their `x-env` variable, and the namespace's JSON Schema so a client can draw the form. The annotations `title`, `description`, `x-group`, `x-env`, `x-danger`, `x-bind` and `x-unit` (`nano_usd`, `ms`, `bps`) are allowed. - A secret-class value in `values` is a marker (`redacted: true`, a keyed 64-hex `fingerprint`, and the optional four-character `hint`, `set_at`, `set_by` and `bound_to`); a marker that carries `value` is invalid, so a summary never carries a secret. - `common.config-change@1` is `{ values, reason, merge?, unset? }`; a secret value is refused by the store (`422 config.secret_write_only`) and is written only through the secret route. - `common.config-secret-put@1` is `{ value, reason }`; the answer is the marker. - **Nine internal capabilities, one per service:** `ai`, `billing`, `services`, `watch`, `bench`, `report`, `host`, `codes` and `actor` each gain `<svc>.config.manage` (owner that service, active, internal, never grantable to apps), implemented by `GET /internal/config`, `GET|POST /internal/config/:namespace` and `PUT|DELETE /internal/config/:namespace/secrets/:key`. Only Network's admin proxy, acting for the owner, holds them; Network uses its own admin config routes. - Contracts keeps no default-grants list: Network's seeded default grants live in OpenVibe.Network, so granting these nine to the network service is that repository's change. ## 0.287.0 — 2026-11-04 - **`billing.credit.settled@1`** (AI Exchange money, D1 and D9). - OpenVibe.Billing emits it from the settle transaction of a Credits hold (`server/ops/credits.js` `settleHold`); OpenVibe.Report consumes it for aggregate analytics. - It carries `settlement_id`, `hold_id`, the hold's `service`, `settled_at`, the answering attempt's `deployment_id` (`<provider>:<model>`, nullable) and `provider_access` (nullable), `rate_version`, the four `usage` token counts, `charge_nano_usd`, `supply_cost_nano_usd`, `shortfall_nano_usd`, `released_nano_usd`, `byok` and `sandbox`. - Deliberately no `subject`, no `project_id` and no account id: the event is for aggregates, while per-person statements are read from Billing. - Money is integer nano-USD as decimal strings (D1); a number amount and a subject field are invalid fixtures. - Billing's manifest lists the event in `eventsProduced`; Report's lists it in `eventsConsumed`. - **`sandbox` on Credits holds: `billing.credit-hold@1` and `billing.credit-hold-create-request@1`** (D10, additive). - An optional boolean `sandbox` (default false) says the hold draws on that person's sandbox credit for the project (account kind `usd_credit_sandbox`, owner `subject:project`), never on real Credits; a real hold never draws on sandbox credit. - `billing.credit-sandbox-grant-request@1` now says its grants land in that sandbox account. - Valid fixtures cover `sandbox: true`; `sandbox: "yes"` is invalid. ## 0.286.0 — 2026-11-04 - **Provider-rate evidence events: `watch.rate.changed@1`, `watch.rate.approved@1`, `watch.rate.rejected@1`** (OpenVibe.Watch #205, plan P6, flag `WATCH_PROVIDER_RATES` off by default). - `watch.rate.changed` is raised when a provider's official price document states a new value for one model and token class. It carries `change_id`, `source_key`, `provider`, `model`, `token_class` (`input`, `cached_read`, `cache_write`, `output`), `from_nano_usd_per_m` (null for a first price), `to_nano_usd_per_m`, `currency`, `region`, `effective_date`, `source_url`, `fetched_at` and `approval_required`. - `watch.rate.approved` and `watch.rate.rejected` are the same change plus `state`, `decided_by` (a person's `usr_` id; Watch refuses a service or an agent), `decided_at` and `applied` (always false). - Money is integer nano-USD per million tokens as decimal strings (D1); all three schemas refuse numbers and floats. - Evidence only: Watch writes no rate card. An approval is dated evidence for OpenVibe.AI's catalog operator, who alone changes the supply rate card through AI's own path. - Watch's service manifest now lists the three events in `eventsProduced`. OpenVibe.AI consumes no events yet (`eventsConsumed` is empty), so it is not registered as a consumer. ## 0.285.0 — 2026-11-03 - **`ai.inference.create` is retired** (AI Exchange D8′). - It duplicated `ai.app.run`, the public capability developer apps already use to call OpenVibe AI (ADR-014, on `/v1/chat/completions` and `/v1/messages`). - It was added in 0.284.0, and was never granted or served. ## 0.284.0 — 2026-11-03 - **AI Exchange contract foundation (D1-D4, D8)**: - `ai.model@1` extended to a catalog entry with optional `id`, `family`, `variant`, `quant`, `revision`, `sha256`, `license`, `context`, `capabilities`, `modalities`, `retail_rate` (nano-USD per million tokens) and `availability`; existing provider-model documents stay valid. - New schemas: `ai.model-artifact@1`, `ai.inference-deployment@1`, `ai.route-explain@1`, `ai.model-list@1`, `ai.model-read-result@1`. - New Billing inference subledger schemas: `billing.credit-hold@1`, `billing.credit-settlement@1`, plus internal route contracts for `POST /internal/credits/holds`, `/holds/:id/extend`, `/holds/:id/settle` and `/holds/:id/release`. - `platform.rate-card@1` compatibly extended with optional `side` (`supply`|`retail`), `version` and `effective_to`; existing documents stay valid. - New public capability `ai.inference.create` for developer-app inference on `POST /v1/chat/completions` and `POST /v1/messages`, and public capability `ai.model.read` for `GET /v1/models` and `GET /v1/models/:id`. - All new schemas ship valid and invalid fixtures, including wrong money type and float money value examples. ## 0.283.0 — 2026-11-03 - **`chat.timeline-result@1` buckets may say how many people wrote** (`people`, optional): a signed-in user, an anonymous viewer or a relayed name each count once. OpenVibe.Live's VOD clip cutter reads it as "a reacted room" (item 151, C08: 12+ messages from 3+ people before an AI clip can be public); without it the count is unknown and the clip stays unlisted. The description now says what Chat already does since Chat #338: Live's own AI viewers (`source_platform` ai) are not counted. Optional, so older readers and writers are unaffected. ## 0.282.0 — 2026-11-03 - **`connect.hook.received@1`** (OpenVibe Connect, CON-15, ADR-057 decision 7): a webhook reached a share's hook at `https://connect.openvibe.website/h/<hook_id>`. Connect keeps the request (body and headers, 7 days) by reference and publishes only a pointer (`body_ref`, method, size, content type), never a body, header or address. Events keeps the event and retries Connect's own subscription for up to seven days, so the request reaches the person's computer once it is back. Connect's manifest now produces it and consumes it, along with the account events it already handled. ## 0.281.0 — 2026-11-03 - **Two legal profiles: `public` and `service`** (`registry.service-manifest@1` and `registry.product@1` `legalProfile`, openvibe-shared 4.107.0). `public` is an open, read-only site with no account. `service` is a personal tool or API that keeps what you make with your account. Ten sites served no `/terms`, `/privacy` or `/dmca` because no profile told the truth about them (the 11-02 sweep). Their manifests move: - `public`: Domains, Report and Search; - `service`: AI, Bench, Design, Events, OpenRestream, Surf and Watch (and AI's product manifest). ## 0.280.0 — 2026-11-02 - **`today.agenda.read`** (first-party, owner today; plan L48 V3, "plan my day"): a first-party service names a person with X-OV-Subject and reads that person's own agenda (`GET /api/v1/me/agenda`), the same one they see at openvibe.today/me. Read-only. OpenVibe.Chat's Vibe reads the day before suggesting a plan, and sets nothing until the person says yes. Schemas `today.agenda-request@1` (the `days` query) and `today.agenda@1` (from, to, days, events, follows_unavailable), with fixtures. ## 0.279.0 — 2026-11-02 - **Website Connect v0: OpenVibe Connect is live** (CON-1 landed with the OQ-13 review; ADR-057 accepted). The nine CON-1 contracts join the catalog, all additive: `website.share@1`, `website.share-create-request@1`, `website.share-connect-request@1`, `website.policy@1`, `website.audit-entry@1`, `connect.device@1`, `connect.policy@1`, `events.hook@1` and `events.hook-delivery@1`, with valid and invalid fixtures. Id prefixes `shr`, `dev`, `hok`, `pol` and `aud`. - **`manifests/services/connect.json`** (live, connect.openvibe.website, port 5240, ov_connect, site position 51 in "make") replaces CON-1's placeholder `website.json`: one id, `connect`, for the repository, ovhost, the database and the OAuth client. It owns `website.*` and `connect.*`; the 20 capabilities stay `planned`, now owned by `connect`, and none is served or granted. - **ADR-057's review** decides v0: the WebSocket fallback through Cloudflare inside Connect (no UDP, no DNS-only record), names `<label>--<user>.openvibe.website` (Space names never contain "--"), a share-scoped connect key until the Link plugin, the gate's host-only signed cookie, modes owner/named and staff-first signin, no public shares until CON-9 and CON-21. ## 0.278.0 — 2026-11-02 - **`live.index_document.upserted` v1 takes type `doc`** (expand only): one of Live's guides, `openvibe.live/docs/<name>` (for instance go-live-in-your-browser), as an OpenVibe.Search document. Its id is the guide's name (`^[a-z][a-z0-9-]{1,63}$`); channel, vod and clip keep their numeric ids. Live's guides were on no network search. Fixtures: `doc` (valid), `doc-bad-name` (invalid). ## 0.277.0 — 2026-11-02 - **`bot.command@1` kind `ai` carries tool calls** (expand only; plan L55 C3, "a local model on your Node"). The value may add `tools` (up to 64 OpenAI Chat Completions function definitions) and `tool_choice` (`auto` | `none`); messages may be `role: tool` with `tool_call_id`, and an assistant message may carry the `tool_calls` the model asked for in an earlier answer. The answer's data adds `message.tool_calls` and `finish_reason`. The machine passes the tools to its model server and hands back the calls; it never runs one. A machine whose Link can pass them says `ai.tools: true` in its capabilities (Link v0.36.0), so a caller sends tools only to such a machine. Older commands validate as before. Fixtures: `ai-chat-tools` (valid), `ai-tool-without-call-id` and `ai-tool-bad-name` (invalid). ## 0.276.0 — 2026-11-02 - **Taglines end without a full stop, every one.** 29 service and product manifests' `tagline` ended with one and the other ~45 did not, so the site switcher (Shared's `icons/sites.json`, built from these) and the share cards mixed both. A tagline with two sentences keeps the one in the middle ("Upload anything. Share it anywhere"). No schema or field changes. ## 0.275.0 — 2026-11-02 - **`report.index_document.upserted` / `.deleted` v1** (active, first-party; plan T9): OpenVibe.Report's Rundown topic pages (type `topic`, `/rundown/t/<slug>`) as OpenVibe.Search documents: the title, headline and summary of what streamers said. Live's own topic pages are noindex; Report's are the public copy. 60 topics were on no network search. - **`design.index_document.upserted` / `.deleted` v1** (active, first-party): OpenVibe.Design's icon pages (type `icon`, `/icons/<name>`) and theme pages (type `theme`, `/t/<slug>`). 280 icons and 50 themes were on no network search. - Both are sent through `openvibe-publishing/search-feed`; the Report and Design manifests list them in `eventsProduced`; the catalog has their entries and fixtures. ## 0.274.0 — 2026-11-02 - **`tours.index_document.upserted` / `.deleted` v1** (active, first-party; plan T9): OpenVibe.Tours' pages as OpenVibe.Search documents. A place to see (type `place`, `/place/<plc_ id>`, from OpenStreetMap: its kind, city and access facts), a city (type `city`, `/city/<slug>`: its Wikivoyage summary, how many places and walks) and a self-guided walk (type `route`, `/routes/<rte_ id>`: stops, distance and time). Sent through `openvibe-publishing/search-feed`; the Tours manifest lists them in `eventsProduced`; the catalog has their entries and fixtures. 2,754 places, 100 cities and 170 walks were on no network search. ## 0.273.0 — 2026-11-02 - **`shoes.index_document.upserted` / `.deleted` v1** (active, first-party; plan T9 "Search: real documents from every product"): a shoe model's page on open.shoes (type `shoe`, `/shoe/<slug>`, the slug as the id) as an OpenVibe.Search document: its name and brand, the lowest price seen today and how many stores sell it. Sent by OpenVibe.Shoes through `openvibe-publishing/search-feed` (a boot sweep and a timer; a model with no live offer is a `thin_content` noindex document, one that left the catalogue a tombstone). The Shoes manifest lists them in `eventsProduced`; the catalog has their entries and fixtures. Network searches for a shoe ("nike air max 90") found deals and coupons but never the model's own page with every store's price. ## 0.272.0 — 2026-11-01 - **`network.dock.timer.write`** (active, first-party; plan L48 R2): a first-party service sets or clears ONE person's Dock timer in the Network, so Vibe can start a focus timer where the person is ("Vibe, start a 25-minute focus timer"). `PUT /api/v1/people/:subject/dock/timer` takes `network.dock-timer-request@1` (the minutes, or `clear: true`) and answers `network.dock-result@1`. The service names the person and writes the timer and nothing else. The first holder is OpenVibe.Chat. ## 0.271.0 — 2026-11-01 - **`network.dock-state@1`** gains an optional `timer` (plan L48 R2): a timer, a focus block or the break after one, with its end, so it counts down in the Dock's pill on every site and rings at its end; a focus timer of rounds alternates focus and break by itself. Additive: absent or null is none. ## 0.270.0 — 2026-11-01 - **`network.dock-state@1`**: the Dock's tab may be `vibe` too (plan L48 V2: the person's Vibe in the Dock on every site, openvibe-shared 4.74.0). Additive: the three tabs before it are unchanged. ## 0.269.0 — 2026-11-01 - **`actor.model.manage`** (active, first-party, sensitive; plan L55 C3): a person chooses the model their OpenVibe.Actor tasks run on — OpenVibe's own keys, or their own provider key (which stays sealed in OpenVibe.AI and never reaches Actor). `GET /api/v1/me/model` reads the choice, `PUT /api/v1/me/model` sets it (`actor.model-manage-request@1`, answered by `actor.model-manage-result@1`). The person's choice alone: another person's is 404. ## 0.268.0 — 2026-11-01 - **`bot.machine.watch_link`** (active, first-party, sensitive; plan L55): the owner's agent (OpenVibe.Actor) asks Bot for a link that shows the desk it works on (the private desk or its own `desk-<name>`, never the owner's own screen), live, inside a chat turn. `POST /api/v1/machines/:id/watch-links` (`bot.watch-link-request@1`, answered by `bot.watch-link-result@1`) makes one for the machine the named person owns — never another person's — and it lives for 5 to 240 minutes. The page needs no sign-in (the token is the credential), may sit in a frame on openvibe.chat or openvibe.actor, and carries the mouse and keyboard only when the caller asks for it. Only the owner's agent may make one: the capability alone would let any service that holds it name any person. ## 0.267.0 — 2026-11-01 - `today.reminder.write` (active, first-party; plan L48): a first-party service sets, lists, snoozes, stops and removes a person's own reminders on OpenVibe.Today for that person, named with X-OV-Subject (`POST /api/v1/reminders/parse`, `GET|POST /api/v1/reminders`, `POST /api/v1/reminders/:id/done|snooze`, `DELETE /api/v1/reminders/:id`). The first holder is OpenVibe.Chat, for Vibe's "remind me…". ## 0.266.0 — 2026-10-31 - **`space.app.claimed`** (plan 5.9, OpenVibe.Space): a guest draft (a space with no owner, edited with a secret link, made on openvibe.space without an account) was claimed by a signed-in person. The draft is that person's space now, with an address from its name, and the secret is gone. The payload holds the space's id, its new slug, its name and the template it started from — never the secret, never file contents. Visibility internal, owner `space`, ADR-004 (Space's events). ## 0.265.0 — 2026-10-31 - **OpenVibe.Tours** (`manifests/services/tours.json`, plan 5.12): openvibe.tours, cities, places and walking routes from Wikivoyage and OpenStreetMap, IRL streams, stays and shared trips. Port 5170, OAuth client `tours`, site position 49 in the streams neighbourhood. - **OpenVibe.Shoes** (`manifests/services/shoes.json`, plan 5.6): open.shoes, shoe offers from Deals with price history, a cited drops calendar and price alerts. Port 5180, OAuth client `shoes`, site position 50 in the shop neighbourhood. ## 0.264.0 — 2026-10-31 - **OpenVibe.Casino** (`manifests/services/casino.json`, plan 5.14): openvibe.casino, the Casino room. Open engines for blackjack, roulette, baccarat, craps and slots with their house edges checked against the published figures, tables with free play chips worth nothing, Learn and a simulator. No real money, no operators, no affiliate links. Port 5210, OAuth client `casino`, namespaces `casino.*`, site position 48 in the games neighbourhood, legal profile `games`. ## 0.263.1 — 2026-10-31 - **openvibe.website is Space's own front door** (plan 5.9; the owner, 10-31: the new domains "just redirect"). Space now answers the apex itself with OpenVibe.Website's home and its website templates, so `openvibe.website` joins `space`'s `domains` (Network's CORS allow-list and Space's reserved custom domains read that field; only the exact origin, never a tenant subdomain). The product manifest is no longer `kind: "moved"`: its description, pillars, questions and links say what the front door is, with the room's accent. ## 0.263.0 — 2026-10-31 - **`report.ai-usage-me-result@2`** (plan L41 R1, OpenVibe.Report): GET `/api/v1/me/ai/usage` as the route now answers it. The body is `{ group, rows, totals, by_paid_by, insights, source }`: `rows` are one bucket each (`day`, `week`, `month`, `provider`, `model`, `surface` or `paid_by`) with `key`, `calls`, `input_tokens`, `output_tokens`, `cached_tokens`, `cache_write_tokens`, `cost_usd`, `fee_usd`, `charged_usd` and `saved_usd`; `totals` repeats those measures for the window; `by_paid_by` holds the same measures for each payer (`own_key`, `openvibe_key`, `plan`, `own_machine`, `free_tier`, `network_allowance`, `org_key`); `insights` are two or three plain lines worked out from the numbers; and `source` is `raw`, `rollups` or `raw+rollups`. - **Breaking shape:** v1's `usage` array and its `input`/`output`/`reasoning`/`cache_read`/`cache_write`/ `list_cost_usd`/`cache_saved_usd` field names are gone, so v1 is deprecated in favour of `@2` and stays loadable until 1.0.0. The v2 TypeScript type is `ReportAiUsageMeResultV2`. ## 0.262.0 — 2026-10-31 - **The task-aware router's explain** (L33 P3 "the router picks for the task", L36 Y7 "a smarter router"; OpenVibe.AI). `ai.run@1` (1.1.0) gains an optional `explain`: why the run answered as it did. - `task`: the requirement set the router read off the request with no model call — `vision`, `tools`, `json`, `min_context` (tokens), `reasoning` (none, some, deep), `output` (text, image, audio, video) and `weight` (light, standard, heavy). - `ranked`: the candidates the router ranked for that task, best first (at most five), each with its reason (the cheapest with vision, the strong tier, warm cache), its price and its catalogue tier. - `attempts`: every provider that was tried, why it was chosen and what went wrong when it failed. - `objective`, `reasons`, `selected`, `candidates` and `paid_by`: the placement view the ranking came from and who paid. `bench` and `usage_readings` stay the optional projections they are. - Nothing else changed: the release is additive, the contract's own id and major stay, and no tag is cut yet. ## 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.link/machines for the person and for apps the person consented to. - **`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 local models, its robots). - **`network.machine-roles@1` and `network.machine-role@1`:** the catalogue of roles. Each role has a name, what it installs on the machine and what the hub shows. A product declares its roles in its own service manifest (`machineRoles`), so the catalogue is the union of what the products declare: `codes.worker` (Codes), `desks.host` (Run), `ai.host` (AI) and `bot.robot` (Bot) are the first four. - **`network.machine-roles-put@1`:** the roles a machine has after a change — the whole set, not a diff. - **`network.machine-pairing@1`:** a one-time pairing code, its expiry and the command to run on the machine. - **`network.machine-list-result@1` and `network.machine-manage-result@1`:** what the hub answers with. - **`bot.machine-manage-request@1` and `bot.machine-manage-result@1`:** what Bot's machines API takes and answers with. `bot.device@1` gains `roles` and `os` (both optional; the old shape still holds). - **The capabilities:** `network.machines.read` and `network.machines.manage` (owner `network`, public: an app may hold them with the person's consent, and may then do only what the person could) and `bot.machine.manage` (owner `bot`, first-party: Network holds it for the hub, so Bot stays the authority and checks the machine is the named person's own). - **The events:** `network.machine.online`, `network.machine.offline`, `network.machine.paired`, `network.machine.roles_changed` and `network.machine.revoked` — Network publishes them through OpenVibe.Events. - **Presence policy:** `network.machine.online` and `network.machine.offline` are a machine's connectivity (the machines hub, plan L34), not a person's presence, and are allowed like `bot.robot.online` and `bot.robot.offline` (ADR-043). Any other `.online`, `.offline` or presence event type is still refused. - **`registry.service-manifest@1` gains `machineRoles`** (optional): the roles a product declares. The manifest keeps its old shape; a manifest without the field means a product with no machine role. ## 0.260.0 — 2026-10-31 - **One AI meter across the network** (plan L40): every AI call on the network leaves one usage record, whoever made it. - **`ai.call.recorded@1`** (`contracts/events/payloads/ai.call.recorded.v1.json`): one call, and who and where it happened (`subject`, `attribution`, `surface`), what ran (`system`, `provider`, `model`, `effort`), who paid (`paid_by`: `own_key`, `openvibe_key`, `plan`, `own_machine`, `free_tier`, `network_allowance`, `org_key`, with `paid_by_detail`), the tokens (`input` uncached, `output`, `reasoning`, `cache_read`, `cache_write` with `cache_write_ttl` `5m` or `1h`, `images`, `audio_seconds`, `video_seconds`), the money in USD (`list_cost_usd`, `charged_usd`, `fee_usd`, `cache_saved_usd`, with `price_source` and `price_date`), how it went (`latency_ms`, `ttft_ms`, `attempts`, `outcome`) and the ids (`run_id` or `turn_id`, `at`). - **No prompt and no answer text, ever.** The payload has no field for either, and the schema closes the object. - **The meter belongs to OpenVibe.Report** (plan L40 and L41): OpenVibe.AI writes the records, Report keeps the ledger and rolls it up. - **`report.ai-usage-open-result@1`**: the public AI totals (tokens, the cache share, the own-key share, the spend by week). No person, no site and no key in it. - **`report.ai-usage-result@1`**: the same meter in groups (day, site, model, system, paid_by) for a service token with the new **`report.ai_usage.read`** capability. A group that fewer than ten people share reads 'fewer than 10' instead of counting them. - **`report.ai-usage-me-result@1`**: the AI usage of one person (GET /api/v1/me/ai/usage), for the person and nobody else. The route answers 501 until Report has sign-in; the shape is fixed here first. - Phase 1 is OpenVibe.AI's own runs; Codes, Actor and the rest feed the same event later. Report rolls the records up, and a record taken twice is stored once (idempotent by `system` and `run_id` or `turn_id`). ## 0.259.0 — 2026-10-31 - **`network.notification-push-request@1` addresses a person by subject as well as by user id (F1, familiar faces).** Vision sends a named person a consent request and Cam sends the camera owner an event alert; both know the canonical `usr_` subject from the token and never hold the numeric Network user id. `POST /internal/notifications/push` accepts `{ subject }` as an alternative to `{ user_id }` (unknown subject: 404), and `POST /internal/notifications/push-bulk` accepts `{ subjects }` as an alternative to `{ user_ids }` (unknown subjects are skipped). The rest of the body and every other rule is unchanged; `subject` is not the actor (`actor_subject` still carries the block check). Two valid fixtures prove the subject forms. ## 0.258.0 — 2026-10-31 - **Labels and choices** (L1, `OpenVibe_Consent_Plan_2026-10-31.md` §"Labels and choices"): light rules, not bans. - **`common.content-labels@1`:** the six labels (`nudity`, `language`, `graphic`, `18+`, `flashing`, `spoilers`) and the shape `{ labels, suggested }` for a stream, VOD, clip, picture or post. `labels` are in force (the creator, a moderator, or three viewer suggestions); `suggested` is what the AI or a viewer proposed and nobody confirmed, so a suggestion never applies itself and never takes anything down. - **`network.label-prefs@1`:** one person's choice per label — `show`, `blur` (until tapped) or `hide` — in Network's site prefs, so one setting covers every site. The web default is `show`; the native apps start `nudity` and `18+` at hide until the person turns them on in their web settings. The home page shows nothing labelled `18+` whatever the prefs say. - **`labels`** is exported from the package (`LABELS`, `CHOICES`, `INFO`, `defaults()`), so a service reads the one list instead of copying it. `test/content-labels.test.js` checks that every list matches and that the fixtures hold the rules the schemas cannot say. ## 0.257.0 — 2026-10-31 - **"Your AI": a person's own provider keys, and how their runs route** (L36 Y1/Y3/Y8; OpenVibe.AI). Five additive v1 contracts and one event: - **`ai.me-credential@1`:** one key as its owner reads it — provider, a four-character hint, base_url, daily budget, status (`ok`, `failing` or `untested`), checked_at, a redacted last_error, models_count and created_at. Never the key. - **`ai.me-credential-put@1`:** `PUT /api/v1/me/credentials/{provider}` for the providers the console knows (Anthropic, OpenAI, Gemini, DeepSeek, Mistral, xAI, Groq, Cerebras, Together, Fireworks, OpenRouter, MiniMax and a custom endpoint). A key is required the first time and when the endpoint changes, and is tested on save. - **`ai.me-credential-test@1`:** the model list (up to five samples) or a one-token request, the balance when the provider reports one, and a redacted error that never carries the key. - **`ai.me-credential-list@1`:** `GET /api/v1/me/credentials`, every key without any key. - **`ai.me-preferences@1`:** `GET/PUT /api/v1/me/ai/preferences` — only_my_keys, free_first, a price ceiling and a default model (or `auto`) per kind of work. - **`ai.credential.failed@1`:** a provider refused a person's key with 401/403 during a run; its status becomes failing and the event says so, never the key. - **Two person capabilities**, `ai.me.read` and `ai.me.manage`: only the person's own Network token, or the person session on AI's own site behind a same-origin check, reaches them. A service token is refused, and so is any token carrying `on_behalf_of`, whatever capabilities it holds (the AI #204/#205 rule). ## 0.256.0 — 2026-10-31 - **`ai.looks` is retired** (`network.consent@1`). The owner removed the rule that AI text never describes people (2026-10-31), so describing how someone looks needs no opt-in. The value stays in the family's lists so readers of 0.255.0 still validate (removing it is a breaking change); nothing writes it, and Network offers it on no page. ## 0.255.0 — 2026-10-31 - **Data and AI by consent** (L24, the consent plan `OpenVibe_Consent_Plan_2026-10-31.md`): nothing a person makes is off-limits to AI or data use by design; each use is off until that person turns it on. - **`network.consent@1`:** one person's choice for one purpose (`ai.train`, `data.public`, `face.recognize`, `ai.looks`, `app.data`), narrowed by kinds of content, an app and options (a licence, face features). It records the wording seen (`text_version`), where it was made, and `granted_at`, `revoked_at` and `expires_at`. A choice is turned off by revoking it, never by deleting it. The id prefix is `cns_`. - **`network.consent-choice@1` and `network.me-consent-result@1`:** "Your data choices" (`GET` and `PUT /api/v1/me/consent`, capability `network.consent.manage`). - **`network.consent-check-request@1` and `network.consent-check-result@1`:** a service asks before each use, for up to 500 people (`POST /internal/consent/check`, capability `network.consent.read`, first-party). An unknown person is false, like one who never chose. Network unreachable means off. - **`network.consent.changed`:** drops cached answers. It names the change, never the content. - **No purpose sells data.** That needs its own contract and a yes for each deal. ## 0.254.0 — 2026-10-30 - **`actor.wait@1`** (plan A2, Watch mode; the design note `research-2026-10-30/agent-a2-watch.md`): one wait — the task and the step it belongs to, whose it is, what wakes it (`until`: an Events topic, a Watch target or a time), the deadline, `on_timeout` (`ask`, `retry`, `fail`) and the short line the person reads. A wait is a record, not a running loop: while a task waits no model runs, its desk is released and its budget clock stops. The event that woke it is untrusted input and is never part of the record; `woken_by` names the event, never its payload. - **A new id prefix**: `wait` → `wai_`, the id a wait carries (`ids.newId('wait')`). ## 0.253.0 — 2026-10-30 - **A2A for OpenVibe.Actor** (Actor v2 stage V2-11). New capability `actor.a2a.task` (owner `actor`, active, first-party, sensitive): a Network service token with it may hand Actor one agent-to-agent task (`POST /a2a/v1/tasks`, the A2A message shape: text parts, an optional skill). The task runs under the person's allowance, budget and allow-list, exactly like a task they started; anonymous callers get 401 and a token without the capability 403. The agent card is `/.well-known/agent.json`. - **Two additive v1 contracts** (new files, no existing schema changed): `actor.a2a-task-request@1` and `actor.a2a-task-result@1`. ## 0.252.0 — 2026-10-30 - **Memory M2 capabilities and contracts** (OpenVibe.AI; plan `OpenVibe_Memory_Plan_2026-10-30.md`, spec `research-2026-10-30/memory-m2-spec.md`). - New active first-party capabilities `ai.memory.read` and `ai.memory.write`, owned by `ai`: the person's own API reads and writes, and a service acting `on_behalf_of` a person with the capability (agent search is at most 8 active items in the scopes its grant names, audited in `memory_reads` with ids only, never text). - Three additive v1 contracts: `ai.memory-item@1` (one decrypted item as its owner reads it), `ai.memory-result@1` (the list, item, profile, deletion count and agent search answers) and `ai.memory-write-request@1` (the batch import, the patch and the source deletion). The sealed fields (`text_enc`, `text_mac`, `source_ref_enc`) are never in a response, and the text is never logged. All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) that consumers install from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0, the notes were in the tag and commit messages (`git tag -n1`). ## 0.251.0 — 2026-10-30 - **`network.dock-state@1`** (the Dock plan 2026-10-30, K-D1): the state of one person's Dock — the little floating surface every OpenVibe site draws. It lives in the Network, not in the browser, because `localStorage` is per site and the Dock follows the person across every domain. `open` (open or collapsed to its pill), `tab` (`agent` | `chats` | `activity`), `pinned` (keep the pill even when nothing runs or waits), `agent` (`{ service: actor|codes|run|desks, id }` or null — the agent the Agent tab follows), `chat` (the conversation the Chats tab has open, or null), `place` (where it sits per device class: a `corner` for the phone, a `corner` plus an optional `width` (320..720) and `height` (360..900) for the desktop) and `updated_at`. Every object refuses extra keys, so a page cannot smuggle a field into the state another page reads. - **`network.dock-result@1`** (the Dock plan 2026-10-30, K-D1): what `GET /api/v1/me/dock` answers on OpenVibe.Network — the person's `network.dock-state@1`, the `counts` the pill's status dot is drawn from (`running`, `waiting`, `unread`) and `sources_partial` (true when an authority behind the counts did not answer, so the counts are lower than the truth). Additive: two new contracts, nothing changed or removed. ## 0.250.0 — 2026-10-30 - **`bot.device-message@1`'s heartbeat gains an optional `fleet`** (Codes C9c): what the machine's coding agents have left — each agent's id, whether it is installed and signed in, and each limit window's label, used and limit and when it frees. OpenVibe Link reads it with OpenVibe.Codes' own logins probe as the machine's user and reports labels, numbers and times only (no key, token or account name); absent means no reading yet, never "nothing installed". Additive: no field changed or removed, and a heartbeat without `fleet` validates unchanged. - **`run.job-create-request@1` gains an optional `node`** (Codes C9c): the device id (`dev_…`) of the job's own machine in Run's placement (`platform.resource-offer@1` node_id, as OpenVibe.Bot's `GET /api/v1/machines` names it), which pins the job to one machine — a conductor must run where its folder and its memory are. Run places a pinned job only on that machine and refuses it `409 run.job.node_offline` when it is not connected, never moving it to another one. Omitted (every existing caller): Run places the job wherever it fits, exactly as before. Additive: no field changed or removed, and a caller that never sends `node` validates unchanged. ## 0.249.0 — 2026-10-30 - **`actor.task-event@2`** (OpenVibe.Actor v2, stage V2-4): a `tool_result` event on a task's live stream now carries `artifacts`: the files and charts that tool made, each `{ kind, name, mime, url, size }`, stored on OpenVibe.Media private to the task's requester. `url` is Actor's stable link, which signs a fresh Media URL for the requester when opened, never an expiring one. Every field of `@1` keeps its name, type and meaning; `@1` is deprecated with `@2` as its replacement. - **`actor.task.read`** now implements `GET /api/v1/tasks/:id/artifacts/:mediaId`, a redirect to that fresh signed Media URL (the same 404 for another person, an unknown id, or a file the task never made). ## 0.248.0 — 2026-10-30 - **`actor.agent-list-result@2`** (OpenVibe.Actor v2): `GET /api/v1/agents` now lists the model each tier resolves to (`tiers`: fast, smart, best, check, vision, with its provider, model, family and availability) and the models the server's keys reach (`models`). Every field of `@1` keeps its name, type and meaning; `@1` is deprecated with `@2` as its replacement. The `actor.agent.read` capability's output schema is `@2`. ## 0.247.0 — 2026-10-30 - Add `bench.run@2`, allowing a null run or task score while human judges have not scored verified outputs. `bench.run@1` stays unchanged. - `bench.run@1` allows a null run or task score while human judges have not scored the published output yet. ## 0.246.1 — 2026-10-30 - The `claude-other` harness offer names its provider `custom`: Claude Code on another provider is the person's own endpoint (`base_url`, a URL that speaks the Anthropic format), and `custom` is the provider id a person's key is kept under. The example and its fixture say so now; nothing else changed. ## 0.246.0 — 2026-10-30 - **Two more harnesses in the catalogue** (OpenVibe.Codes): `platform.harness-offer@1` publishes MiniMax Code (`minimax-code`, the `mcode` CLI on the person's own MiniMax plan) and Claude Code on another provider (`claude-other`, the same `claude` CLI in the cm-style environment: the person's own key, another provider's model) as two of its examples, with the fields their siblings have. - **`platform.harness-offer@1` gains the optional `agents`** — the agents (models) a harness offers, each one a `platform.agent-offer@1` whose `harness` names the offer. A catalogue row is one contract instance now: the offer and the models it runs. Additive: every harness offer that leaves it out still validates. ## 0.245.0 — 2026-10-30 - **`codes.task-start-request@1` accepts `permission: "review"`** — a read-only coding task (research, an audit, a review), which Codes runs at its `read` permission level: the agent looks and answers and changes nothing. Additive to the enum (`review` beside `edit` and `full`); every existing request still validates. ## 0.244.0 — 2026-10-30 - **Bench as an open benchmark platform, stage B1** (spec 2026-10-30): `bench.benchmark@1` and `bench.run@1`. A benchmark is an owned, versioned and forkable set: its tasks, its judges (pinned by version), how they combine into one score, the limits a run may not pass, the default model and harness matrix, and whether it is public or unlisted. A run is one result: a benchmark version × a model × a harness × settings × who ran it, with the per-task scores, cost, time, tokens, the artifacts, and its provenance — `verified` (an executor ran it) or `submitted` (posted with no executor, badged and out of the default rankings). - **Three new capabilities** for Bench: `bench.benchmark.write` (create, edit, freeze and fork a benchmark), `bench.run.publish` (publish a run) and `bench.run.read` (the run, the comparison, the rankings and the percentile). OpenVibe.Bench's service manifest lists them. ## 0.243.0 — 2026-10-30 - The release cut from `main` for codes.task@2 (#250): a coding task's attempts and outcome, one id with two majors. It carries 0.241.0 (the AI credential v2 release) as well. `v0.242.0` and `v0.242.1` were tagged from the PR branch before it merged; pin `v0.243.0` instead (OpenVibe.Codes moves to it). ## 0.242.1 — 2026-10-30 - The generated OpenAPI files carry the release's own version again: `v0.242.0` shipped them with 0.240.0's, because the version was raised after they were written. Nothing else changed; `v0.242.1` is the release to pin. - This release is rebased on `0.241.0` (the AI credential v2 release, merged while it was being written), so the branch carries that work too; the published `v0.242.1` tarball, which OpenVibe.Codes pins, is the cut from before the rebase and holds the codes.task@2 work alone. The next tag cut from `main` carries both. ## 0.242.0 — 2026-10-30 - **`codes.task@2`** (OpenVibe.Codes): a coding task's timeline. It is `codes.task@1` with the same fields, plus two added ones: `attempts` (every try of the task, in order: the agent, the machine, the session, the state it ended in, why, what it cost) and `outcome` (what the attempts added up to: done, unfinished with what is left, or stopped). `GET /api/v1/tasks/:id` answers it — the task's own fields stay at the top level, exactly as `codes.task@1` had them, and the two new fields are added to them. `codes.task@1` stays as it is. - **One contract id may hold more than one major** now: the catalog lists a v1 and a v2 file for the same id, the bare id resolves to the newest major, and `id@N` names that major, so a v1 caller keeps resolving while a v2 caller names it exactly. (`ai.credential@2` is the first to use it.) ## 0.241.0 — 2026-10-30 - **AI: one credential per provider** (Codes C3; research `research-2026-10-30/codes-c3-connections.md` items 1 and 2). `ai.credential-put@2` and `ai.credential@2` are additive on `@1`: the provider enum gains `deepseek`, `minimax`, `openrouter` and `custom` beside `anthropic`, `openai` and `node`, and a PUT may carry `key` (v1's `api_key` stays accepted) and its own `budget_usd_per_day`. `custom` requires an https `base_url`; `node` still takes a machine and no key. A person may now keep one key per provider, and the route without a provider still stores the default one. `ai.credential@2` returns the same body as `@1` with the widened provider, and `key_hint` for every provider except `node`. - **Tooling:** the registry can hold more than one major of one contract id — the bare id resolves to the newest listed and `id@N` to that major — so `ai.credential-put@1` keeps resolving while `@2` is the one callers name. The bundle keys a multi-major id as `id@N`, the catalog check looks each entry up by its own major, and a fixture set is checked against the newest major of its id. `ai.credential-put@2` is the first contract to use this. ## 0.240.0 — 2026-10-29 - **OpenVibe.Vision's API** (plan §5.8b): the service manifest now lists the 20 capabilities its routes serve today — `vision.image.ask`, `vision.text.read`, `vision.model.compare`, `vision.ui.check`, `vision.alt.write`, `vision.media.find`, `vision.receipt.read`, `vision.product.match`, `vision.camera.watch`, `vision.video.moments`, `vision.face.detect`, `vision.face.blur`, `vision.face.consent.manage`, `vision.face.verify.self`, `vision.face.find.self`, `vision.face.library`, `vision.face.cast.match`, `vision.face.cast.read`, `vision.models.read` and `vision.benchmarks.read` — with their input and output schemas: `vision.image-request@1`, `vision.ask-request@1`, `vision.ocr-request@1`, `vision.compare-request@1`, `vision.find-request@1`, `vision.receipt-request@1`, `vision.product-request@1`, `vision.watch-request@1`, `vision.moments-request@1`, `vision.answer@1`, `vision.answer-result@1`, `vision.alt-result@1`, `vision.find-result@1`, `vision.receipt-result@1`, `vision.product-result@1`, `vision.watch-result@1`, `vision.moments-result@1`, `vision.face-detection@1`, `vision.face-blur@1`, `vision.face-enrol-request@1`, `vision.face-verify@1`, `vision.face-find-result@1`, `vision.face-cast-match-request@1`, `vision.face-cast-match@1`, `vision.face-tag@1`, `vision.face-tag-list@1`, `vision.face-library-request@1`, `vision.face-library-result@1`, `vision.face-library-group@1`, `vision.models@1` and `vision.benchmarks@1`. - **Consent** `vision.consent@1`: whose face or voice (subject and modality), the purpose-scoped feature and scope, who signed which text version, when it expires, when and how it was revoked (subject, retention, account deletion), the retention limit and whether the template and derived data were erased. `vision.consent-request@1` and `vision.consent-revocation@1` are the grant and revoke bodies. - **Events** `vision.result.ready@1` (a result's identifiers, engine, provenance, cost, latency, retention and consent id; never an image or answer text) and `vision.alert@1` (an owner-granted camera alert: source reference, classes and confidence). `events.event-envelope@1` now accepts a two-segment event type (`vision.alert` is the first), a backward-compatible widening; every capability id still needs three segments. ## 0.239.0 — 2026-10-28 - Added `graph.link@1` (`from`, `rel`, `to` OVRN relation) for the planned object graph. The `graph.link.created` event waits for a producing service. - Added `report.metric@1` from OpenVibe.Report's `docs/report.metric.v1.json` proposal, including exclusive UTC day and 7/30/90-day window forms. Report v1 currently reads upstream numbers and does not publish metric rows. - Added the optional `site.neighbourhood` field to `registry.service-manifest@1` (1.2.0), last in each of the 48 service site blocks. The seven ids match OpenVibe.Shared `rooms.js` and Network's neighbourhood menu. - Today already had all three live `today.event.*` outbox payloads and fixtures. Today, Report, Design, Domains and Vision already have manifests; Website is served by Space, so no duplicate service manifest was added. ## 0.238.0 — 2026-10-28 - **OpenVibe.Bench** (`bench`, live at bench.openvibe.services, port 5230, database `ov_bench`): the shared AI benchmark and comparison engine for every room (plan §4.12). It owns kinds, pinned rounds, entries, blind judgements, ratings, boards, the open dataset per kind and private evals; AI stays the run authority and Report the analytics authority. New `bench.kind@1`, `bench.task@1`, `bench.round@1`, `bench.entry@1`, `bench.judgement@1` and `bench.rating@1` schemas, the `bench.round.finished`, `bench.judgement.recorded` and `bench.rating.changed` event payloads, and nine first-party `bench.*` capabilities. The manifest is modeled on OpenVibe.AI (`bench.openvibe.services`, not `openvibe.bench`) and its site block (Bench, icon `bench`, position 47, legal profile `tools`) sits in For developers. Bench calls `ai.run.create` (namespace `ai.generate`) and subscribes to the account events; Design moves its Arena onto Bench with `bench.task.create`, `bench.round.create`, `bench.round.read`, `bench.judgement.cast` and `bench.board.read`. - **Codes** (`codes.task-start-request@1` 1.1.0, `codes.task@1` 1.1.0): optional `actor_task_id`/`actor_step_id` request fields and the `actor_ref` back-reference, so a task started from OpenVibe.Actor can carry its Actor task and step. Both are optional, so existing producers and consumers are unaffected. ## 0.237.0 — 2026-10-28 - **OpenVibe.Cam** (`cam`, live at openvibe.cam, port 5160, database `ov_cam`): the camera app (preview, timer, 3-shot burst, grid and an on-device colour/light/softness filter), a private wall for the cameras a person owns, and the still Live's cam button takes. A camera is private to its owner and visible to a viewer only while the grant naming them is unrevoked and unexpired, checked on every read, with no staff bypass. Photos and stills are uploaded to OpenVibe.Pics with the person's own Network token and stored by reference (Pics slug, page and direct links, filter, caption) — Cam keeps no picture bytes and runs no face recognition. The manifest names `cam.*` and the two account events it consumes, and the site block (Cam, icon `cam`, position 46, legal profile `ugc`) in Streams & places. Live calls `POST /api/v1/stills` with the viewer's own Network token, which Cam verifies against Network's JWKS; no service token is involved, so it registers no capability of its own. The account export/deletion pair, `identity.subject.resolve`, Events subscriptions and the (absent) Search relay are Network grants. ## 0.236.0 — 2026-10-28 - **OpenVibe.Design** gets the Design Arena and its database `ov_design`: rounds, outputs, votes and Elo ratings. Migration 0001 only creates the Arena's own tables, so a rollback has no blocker. Design calls `ai.run.create` (namespace `ai.generate`) to pin three of AI's generate offers. The contracts range is now `>=0.236.0`. - **OpenVibe.Fun** (`fun`, live at openvibe.fun, port 5140, database `ov_fun`): IRL quest boards on a streamer's public Live channel, a deterministic local guide from OpenStreetMap (Ticketmaster only when a key is configured) and group plans the group votes on, with the winner added to the viewer's own Today using the viewer's token. Public quests and plans are `search.index-document@1` rows queued in `fun_outbox` with tombstones; Live and Chat are public reads, never writes. The manifest names `fun.*`, the two account events it consumes, and the site block (Fun, icon `fun`, position 45, legal profile `ugc`) in Games & laughs. No route needs a service token, so it registers no capability of its own; the account export/deletion pair, Events subscriptions and Search relay are Network grants, and the Search relay waits for its writer. ## 0.235.0 — 2026-10-28 - **OpenVibe.Vision** (`vision`, live at openvibe.vision, port 5130, database `ov_vision`; plan 2026-10-28 §5.8): ask a picture, read its text with the local Tesseract engine first, compare the local reader with up to three AI hints, and check HTML, a public URL or a screenshot for overflow, cut-off text, low contrast, overlap and layering faults. OpenVibe.AI is the only model gateway (`ai.run.create`, workflow `ai.complete`); Vision holds no provider key, uses no face recognition and stores no image bytes. The manifest names `vision.*`, the two account events it consumes, and the site block (Vision, icon `vision`, position 43, legal profile `ugc`) in News & answers. It registers one capability, `vision.ui.check` (first-party, the route Design's Arena calls), with `vision.ui-check-request@1` and `vision.ui-check-result@1` (new); `vision.ask`, `vision.ocr` and `vision.compare` are served but wait for a caller. - **OpenVibe.Exchange** (`exchange`, live at openvibe.exchange, port 5150, database `ov_exchange`): free and swap listings with a permission passport, conformance tests for each version, a manifest/passport sandbox dry run, install with the exact passport snapshot, one-tap revoke, claims and swap offers, a prohibited-items/no-money filter and staff hide. Every public listing is one `search.index-document@1` queued in `exchange_outbox`; v1 writes no Network grant and executes no listing code, so it registers no capability of its own. The manifest names `exchange.*`, the two account events it consumes, and the site block (Exchange, icon `exchange`, position 44, legal profile `ugc`) in Shop & swap. ## 0.234.0 — 2026-10-28 - **OpenVibe.Meme** (`meme`, live at openvibe.meme, port 5120, database `ov_meme`): the open meme database, rebuilt with AI. Entries cite the chat line or VOD second they came from, follow later uses on a timeline and keep a status; candidates stay hidden until a staff person publishes them; a daily job re-verifies every citation; people vote once an ISO week for meme of the week and captions are plain text over a template. Every public entry is one `search.index-document@1` queued in `meme_outbox`. The manifest names `meme.*`, no capabilities or events of its own, and the site block (Meme, icon `meme`, position 42, legal profile `ugc`) puts it in Games & laughs. - **Search's hybrid ranking** (additive): `search.fabric-result@1`'s `route.explain` entries gain optional `ranking` (`hybrid` or `keyword`) and `reason` (`no_vectors`, `ai_unavailable`, `short_query`), and its results gain optional `ranks` (the 1-based keyword and vector positions, null when absent from that list). `search.query-result@1` gains the same per-hit `ranks` and an optional `explain` object. Keyword-only answers stay valid. - `domains.proposal@1`: a change may be `type: "NS"` (a domain's nameservers at the registrar, update only), with `before`/`after` as the list of nameservers (additive; OpenVibe.Domains' Porkbun connection). ## 0.233.0 — 2026-10-28 - **OpenVibe.Domains' control panel** (plan 2026-10-28 §5.1, DOM-2): - **`domains.proposal@1`** (new, public): a DNS change waiting for its person. It holds the record diff (create, update, delete), why, the risk, its status (pending, applying, applied, declined, failed, rolled_back, expired) and who asked. The credential is never part of it. - **`domains.actions.read`** and **`domains.proposal.decide`** (new, first-party, for OpenVibe.Network): Domains' proposals in the Action Center, approved or declined there. - `common.action-item@1`'s `service` and `network.me-actions-result@1`'s `sources` gain `domains` (additive). - The Domains manifest names both capabilities and its database (`ov_domains`). The provider credential is a Network project secret (ADR-056), granted to `domains` for reveal to the provider's origin only. ## 0.232.0 — 2026-10-28 - **codes.task.queued|running|succeeded|failed|cancelled** (new payload contracts, first-party, owner `codes`; plan 2026-10-28 §6, decisions 2 and 3). OpenVibe.Codes announces each state change of a coding task: `{ task_id, state, agent, repo, cost_usd, files_changed, started_at, finished_at, error }`. The envelope's subject is the owner; the prompt is never in a payload. - The Codes manifest now names what it produces (the five topics) and consumes: `run.job.*`, which finalize a task without polling, and the two account events. Its notes and startup recovery cover the session lines and the outbox. ## 0.231.0 — 2026-10-28 - **OpenVibe.Surf** (`surf`, live at openvibe.surf, port 5110; plan 2026-10-28 §5.6, the Surf room). One room, three surfs: - Ride: OpenVibe.Games' CS-style surf maps in the browser, with a millisecond timer and Play time boards; - Web (Surf with Vibe): one OpenVibe.Actor task, made with the person's own Network token, browses on a private desk and reports with its sources; - Watch: a lean-back TV over OpenVibe.Live's live streams, clips and AI moments. v1 is a stateless front and an OAuth client of OpenVibe.Network (`surf`); it stores nothing. The site block (Surf, icon `surf`, position 41, legal profile `account`) puts it in Games. `surf.*` is its namespace. ## 0.230.0 — 2026-10-28 - **OpenVibe.Today** (`today`, live at openvibe.today, port 5100, database `ov_today`; plan 2026-10-28 §5.3, the Today room). v1 is the network's calendar authority: - stream and event schedules; - reminders sent as Network notifications; - calendar feeds: a channel's public one and a person's private one (RFC 5545); - the daily brief. It publishes `today.event.scheduled`, `today.event.updated` and `today.event.cancelled` (new payload contracts, first-party) from an outbox. It consumes `live.stream.started` and `live.stream.ended` to move a stream live and then ended. The site block (Today, icon `today`, position 40, legal profile `account`) puts it in News & answers. `today.*` is its namespace. ## 0.229.0 — 2026-10-28 - **The Action Center** (plan 2026-10-28 §3.10, NOTIF-5): one list of a person's agent work on every site. - `common.action-item@1`: one item (an agent task, a coding task, an AI run, an approval), with its bucket (running, waiting, scheduled, done, failed), state, cost in USD, steps, error, page and the actions it allows. - `common.action-list-result@1`: one authority's items for a person. Served by Actor, Codes and AI at `GET /api/v1/actions?subject=usr_…` under the new first-party capabilities `actor.actions.read`, `codes.actions.read` and `ai.actions.read`, which are granted to Network only. - `actor.approval.decide`: Network approves or declines an Actor approval for its person. - `network.actions.read` and `network.me-actions-result@1`: `GET /api/v1/me/actions` merges them with Network's own confirmations into the five buckets, and `POST /api/v1/me/actions/:service/:id/:action` acts on one. ## 0.228.0 — 2026-10-28 - **OpenVibe.Design** (`design`, live at openvibe.design, port 5090; plan 2026-10-28 §5.2, the Design room). v1 is the theme studio, a stateless front and an OAuth client of OpenVibe.Network (`design`). It has: - every theme in the catalogue, tried on a Live channel, a Chat room and a shop at once, with colour-vision views; - a six-colour editor that derives the full token set, with WCAG checks that must pass before the theme is used; - Use (Network's `PUT /api/themes/me`, source `studio`), sharing to the gallery, and History with Restore. The site block (Design, icon `design`, position 39, legal profile `account`) puts it in the menu's Make neighbourhood. `design.*` is its namespace: the Arena (AI designers judged by people) comes next. ## 0.227.0 — 2026-10-28 - **OpenVibe.Report** (`report`, live at openvibe.report, port 5080; plan 2026-10-28 §5.4, the Report room). v1 is a stateless front over OpenVibe.Network's open stats and OpenVibe.Live's Rundown and after-show reports. It has: - the network's numbers, open to everyone, each with its window and definition; - The Rundown: topics, takes, episodes and voices; - each stream's after-show report: viewers minute by minute, chat, the moments with links into the recording, and the AI write-up, labelled; - an open JSON API. The site block (Report, icon `report`, position 38, legal profile `info`) puts it in the menu's News & answers neighbourhood. `report.*` is its namespace: v2 makes it the analytics authority. ## 0.226.0 — 2026-10-28 - **OpenVibe.Domains** (`domains`, live at openvibe.domains, port 5070; plan 2026-10-28 §5.1, the Domains room). v1 is a stateless front over OpenVibe.Host's price catalog and OpenVibe.Tools' domain history: - one box prices a name across every ending, with 1-, 3- and 5-year totals and renewal traps flagged; - a page per ending lists every registrar; - a page per domain gives its public record; - an open JSON API. The site block (Domains, icon `domains`, position 37, legal profile `info`) puts it in the menu's Make & build neighbourhood. `domains.*` is its namespace: the v2 control panel (registrar connections, portfolio, proposals) is that authority. ## 0.225.0 — 2026-10-28 - **The Search Fabric** (plan 2026-10-28 §4.5; the owner, 10-28: "a shared sdk/api enabled multi purpose search system for OpenVibe to reuse across sites and more"): one search over several scopes, routed the cheapest way that answers. - **`search.fabric.query`** (public, owned by `search`): `POST /api/v1/query` (or `GET /api/v1/query?q=&scopes=`), and `GET /api/v1/query/providers`. It is the SDK's `createSearchClient().query()` and the MCP tool `openvibe.search`. - **`search.fabric-request@1`:** - `q` and `scopes`: `network`, `mine`, `web`, and `news`, `images`, `video`, `products`, `people`, `places`, `code` (accepted now; v1 answers them as network results by kind); - `filters` (owner, type, lang, site); - `freshness`; - `depth`: `quick` reads only the indexes and caches, `standard` climbs the provider ladder until a step answers, `deep` asks every step; - `answer`, `limit` and `cursor`. - **`search.fabric-result@1`:** - `results`, each with its scope and kind (a document in Search's index, or a web page with its provider, URL, date and when it was read); - `groups` (counts per scope) and `citations`; - an optional `answer`. It is labelled "Written by AI from these sources" and is never without citations. - `route.explain`: step by step, what was asked, what answered, from the cache or not, why a step was skipped, and what it cost. The query is never echoed (the `?q=` rule). - **`search.provider-offer@1`:** a web provider as the ladder and Route see it. It gives the scopes, the price per call, whether the provider has its own index, availability (balance, key, rate limit, daily cap), latency, answered rate and terms. - **Who may use what:** - anonymous callers get network results, plus web results from the open index and the cache; - a signed-in person, or a first-party service, also reaches the paid providers within OpenVibe's daily caps; - `mine` needs a person; - providers never receive an account identifier. Fixtures, valid and invalid, for all three schemas. ## 0.224.0 — 2026-10-28 - **A `crawl` source type for OpenVibe.Sources** (the owner, 2026-10-28: "aggregate all websites and select deals on all sites… a full comprehensive crawler system… the open source Google"). A crawl source walks one site from its endpoints (the seeds): - **How pages are found:** the site's own sitemaps (robots.txt `Sitemap` lines, else `/sitemap.xml`, indexes followed), and, if asked, its same-host links. - **Which pages:** `include` and `exclude` patterns. - **Limits:** `max_pages`, `pages_per_run`, `recrawl_sec` and `max_depth`. - **What is kept:** `require_structured`, only pages that publish schema.org JSON-LD. - **The rules every source already follows still apply:** robots.txt on every request and redirect hop, per-host spacing and Crawl-delay, a terms note before it can be enabled, and noindex pages never kept. - **What is read:** only what a page publishes for machines (products and offers, articles, reviews). - **Price history:** a page fetched again on schedule turns a price seen twice into a history (Deals' observations). - **Fields:** `type` gains `crawl` and sources gain `crawl` on `sources.source@1` and its write request. There is a valid fixture. ## 0.223.0 — 2026-10-28 - **Run sessions** (the Desks plan's D6; `OpenVibe_Agent_Desks_Plan_2026-10-27.md`). A session is a browser desk that OpenVibe.Run places for one person and OpenVibe.Bot leases to it: - `run.session@1` (states placing, starting, running, ended; where it runs and with what trust; desk-seconds), `run.session-create-request@1` (`POST /api/v1/sessions`: the person's own named machine, or placement over desk hosts, `ttl_s`, `idle_s`, requirements, an idempotency key), `run.session-control-request@1` and `run.session-control-result@1` (`POST /api/v1/sessions/:id/commands`: screen, input and browser on the session's desk only), `run.session-list-result@1`, and the events `run.session.started` and `run.session.ended`. - Capabilities `run.session.create`, `run.session.read` and `run.session.control` (public, the caller's own sessions). - Desk time is metered as one `platform.usage-sample@1` a minute (service `run`, operation `browser.desk`, unit `s`, idempotency key `run:<session>:<minute>`). - **Desk leases on Bot**: `bot.desk-lease@1` and `bot.desk-lease-create-request@1`, with the capability **`bot.desk.session`** (first-party, sensitive): Run leases one named desk for one session on the person's own machine (where they let agents use it) or on a desk host whose owner offers it to other people and whose OpenVibe Link runs every desk in its own sandbox (Link v0.32.0, `desk_isolation: sandbox`); only that session's commands reach the desk, never the machine itself. - Fixtures for each, and `test/platform-run.test.js` holds the two sides together (ids, desk names, sizes, kinds). ## 0.222.0 — 2026-10-27 - **Site names say their domain** (owner, 2026-10-27: "it says memberships … couldn't it say VIP memberships since the domain is VIP? … consider these kind of small things, especially when it comes to domain names across all the tools and websites"). The `site.name` the launcher, menus and pages show is now the domain's own word: - VIP (was Memberships), Trade (Markets), Host (Hosting), Space (Spaces), Bot (Robots), Actor (Agents), Work (Jobs) - Quest (Quests), Rent (Rentals), Watch (Alerts), Inventory (Items), AI (AI API), Run (Compute), Play (Game services) - Zone (Storage), Codes (Code) - Each tagline still says what the site is for: "Memberships for creators and their software", "Watchlists and alerts". - Live, Video, OpenRestream and the rest already matched. ## 0.221.0 — 2026-10-27 - **Any of your own machines** (AI on your own machines). A node credential's `machine`, in `ai.credential-put@1` and `ai.credential@1`, may be **`any`**: whichever of the person's machines is on, lets their AI use its models and offers the model. - The fastest goes first, by the speed OpenVibe.Bot reports (`computer.ai_speed`, OpenVibe Link v0.29.0). - The next one is tried when one is busy or away. - It is never anyone else's machine, and never another provider. - Fixtures: a valid `any`, and an invalid machine that is neither a `dev_` id nor `any`. ## 0.220.0 — 2026-10-27 - **A person's own provider may be their own machine** (AI on your own machines, step 4). `ai.credential-put@1` and `ai.credential@1` take provider **`node`** with **`machine`**, a `dev_` id. - There is no key or base_url, and no cost. - OpenVibe.AI reaches the model only through OpenVibe.Bot (capability `bot.machine.ai`), only for that person, and only where they turned on "Let my AI use this computer's models". - A node credential has no `key_hint`; the others still require one. - Fixtures: a valid node credential (put and read), a node credential with a key, and one without its machine. ## 0.219.0 — 2026-10-27 - **AI on your own machines** (the roadmap's user-owned GPU route; `OpenVibe_AI_On_Your_Machines_Plan_2026-10-27.md`). `bot.command@1` gains kind `ai`: one chat completion (`model`, `messages`, `max_tokens`, `temperature`) on the person's own machine. OpenVibe Link v0.27.0's `ai` plugin relays it to a local model server (Ollama, llama.cpp, LM Studio) and answers `{ model, message, usage? }`. - New capability **`bot.machine.ai`** (first-party, sensitive, owned by `bot`). A service, OpenVibe.AI, runs a model on a person's own machine for that person, where they turned on "Let my AI use this computer's models". It never places one person's request on another's machine, and the audit keeps the model and token counts, never the text. - Fixtures: a valid chat, and an invalid one with no messages. ## 0.218.0 — 2026-10-27 - **Programs on a desk** (the Desks plan's desk templates; OpenVibe Link v0.26.0). `bot.command@1`'s `exec` takes: - `desk`: the program runs in that desk's session (its display, session bus and temp folder), so a program with windows opens on the desk. - `detach`: on a whole desk only. It starts a program that keeps running, such as an office suite, a terminal or an editor, and answers at once with its pid. The program runs until the desk stops. - An agent can now work in real applications on a desk of its own: open them with `exec`, look with `screen`, use them with `input`. - Fixtures: a valid detached program on a named desk, and an invalid one with a bad desk name. ## 0.217.0 — 2026-10-27 - **Kept desk profiles** (the Desks plan's step D7, on one machine; OpenVibe Link v0.24.0). On `start`, kind `desk` takes `profile`: a browser profile kept on the machine by that name. - The desk's browser starts from that profile, with its cookies and site storage, so a person signs in once, on any desk (an agent's "Needs you" sign-in carries over to the next task). - The desk saves the profile back when it stops; when several desks stop, the last one wins. Caches are never kept. - The private desk keeps its own profile. A machine that keeps profiles says so in its computer capabilities (`desk.profiles`). - Fixtures: a valid start from a kept profile, and an invalid start with a bad profile name. ## 0.216.0 — 2026-10-27 - **Browser desks** (the Desks plan's step D2; OpenVibe Link v0.23.0). Kind `desk` takes `kind` on start: - `desktop` (the default) is a whole screen: a display, a window manager and a browser shown on it. It needs Linux with Xvfb. - `browser` is a browser alone, with no display, at about half the cost. Its screen, mouse and keyboard, and its live view, are the page's own. It runs on Linux or macOS with Chromium, so a Mac can host desks too. - A machine says which kinds it runs in its computer capabilities (`desk.kinds`). - Fixtures: a valid browser desk start, and an invalid start with an unknown kind. ## 0.215.0 — 2026-10-27 - **Several desks per machine** (the Desks plan's step D1, `OpenVibe_Agent_Desks_Plan_2026-10-27.md`; OpenVibe Link v0.19.0). `bot.command@1`'s `desk` on `screen`, `input` and `browser` takes a named desk, `desk-<name>` (lower-case letters, digits and dashes, up to 24 after `desk-`), besides `main` and `private`. Kind `desk` takes `desk` (which desk; `private` by default) and the action `list` (the desks that run). Each desk is a separate computer for its agent: its own display, browser profile, downloads and temp folder. A machine runs at most its `max_desks` at once. Fixtures for a named start, list, a browser on a named desk, a bad name and `main` as a desk to start. ## 0.214.1 — 2026-10-27 - Site text only (`manifests/services/*.json`), so the Network's home and menus say what each product does today: - **Games:** the tagline is "Browser games, and games you make". The description and the build-your-own FAQ name the creator platform shipped on 10-27: starter ground, a team of up to eight, remix with credit, the asset store, and first-person or top-down. World scripts are left out while they are switched off in production. - **Tools:** more than 180 tools (184 on the home today), with barcodes and QR codes. - **Robots:** drive by keyboard, gamepad or on-screen joystick, from the robot's page or under a live stream. - `docs/ESTATE.md` and `manifests/repositories/` regenerated from every repository's main (Contracts#219). ## 0.214.0 — 2026-10-26 - **`chat.vibe.deliver`** (first-party, sensitive, owned by `chat`; consumer plan §4: "each run is a chat you can open"): a first-party service puts a result of the person's own work into their Vibe chats (`POST /internal/chat/vibe/deliver`). OpenVibe.Actor posts each finished run of one of their OpenVibe Agents into one chat per agent, named after it, as Vibe's message. No AI answer is made and nothing counts against the allowance. Schemas `chat.vibe-deliver-request@1` (subject, source { kind agent, id, name }, text in Markdown, url, idempotency_key) and `chat.vibe-deliver-result@1` (chat, url, repeated), with fixtures. `actor.agent-manage-request@1` and `-result@1` gain `send_to_vibe` (boolean, default true), the agent's switch for it. - **`actor.agent.manage`** lists the suggestions routes (`GET /api/v1/me/agents/:id/suggestions`, `POST …/suggestions/:pid/:action` with apply | dismiss | undo): a personal agent may suggest a line for its instructions after a run that passed its check, which waits for the person (OpenVibe.Actor#115). - **Run's manifest names its loopback origin** (`internalOrigin` http://127.0.0.1:4920, `health` /api/health, `ready` /api/ready): Network's status page showed Run as unknown ("no internal address known to Network") although it runs. Its `lifecycle` said "not built yet" throughout; it now says what Run does (shutdown drains, what it resumes at boot, rollback, no leases). - **Vibe by voice on a person's own machine** (the owner, 2026-10-24: "a call word on Live and on Node"; consumer plan §8). `bot.device-message@1` gains two frames and two config fields, all additive: - `voice_ask` (device to server): `id` and `text`, what the person asked after the machine's call word, transcribed on the machine (nothing leaves it before the call word); optional `lang` (BCP 47) and `robot_id`. - `voice_answer` (server to device): the same `id`, `state` working | answered | refused, the `text` to speak and the `url` of the Vibe chat that keeps it. - `config` carries `voice` (the owner turned it on for this machine) and `call_word`. Fixtures for each, and two invalid ones. - **`chat.vibe.ask`** (first-party, sensitive, owned by `chat`): a first-party service asks Vibe on a person's behalf (`POST /internal/chat/vibe/ask`): Bot's Vibe by voice, with the machine's owner as `subject`. Chat keeps the question and the answer in that person's own Vibe chats (one chat per source, "Voice on <machine>") and counts the answer against their daily allowance. Schemas `chat.vibe-ask-request@1` (subject, text, source { kind voice, id, name }, lang, idempotency_key) and `chat.vibe-ask-result@1` (state answered | refused, text, url, chat, code), with fixtures. - **Quiet hours for personal agents** (consumer plan step 4: "recurring work … with quiet hours and a budget"). `actor.agent-manage-request@1` and `-result@1` carry `quiet_from` / `quiet_to` (HH:MM UTC, both or neither; the window may cross midnight): no scheduled run starts inside it, an hourly run waits until it ends, and a run the owner starts still runs. Fixtures for both. - **A private desk for agents on a person's own machine** (plan track K, consumer plan step 2: Vibe's desk on Node). `bot.command@1` takes kind `desk` (`action`: start | stop | status; `width`, `height` on start): a display of its own on the machine (Xvfb, a window manager, its own session and browser profile) where an agent works without touching the person's screen, which the person can watch and take over. `screen`, `input` and `browser` take `desk: private` to act there instead of on the person's screen (`main`, the default). Starting and stopping act (refused at observe, approved one by one at ask); status runs at every level. Fixtures for both. - **Support inboxes open to developer apps** (the owner, 2026-10-24/25: openvibe.chat "fully modular and SDK and API"). Three public, sensitive capabilities owned by `chat`, on the routes OpenVibe.Chat already serves; an app acts only for the person who authorized it (`on_behalf_of`), and that person's role in the inbox decides the rest: - `chat.inbox.read`: the inboxes the person works in, conversations (waiting first), messages, saved replies, and the owner's export (JSON or CSV); - `chat.inbox.reply`: reply, resolve, reopen, give back to Vibe, assign, mark read, translate; - `chat.inbox.manage`: make an inbox, and as its owner change it (Vibe, sites, hours, routing, retention), its people, saved replies, webhooks, actions and verified visitors. - **Schemas:** `chat.inbox-request@1`, `chat.inbox-result@1`, `chat.inbox-reply-request@1`, `chat.inbox-conversations-result@1` (public), with fixtures. - **`capability@1`:** `resourceConstraints` may say `inbox_member`. ## 0.213.0 — 2026-10-25 - **Plain names in every menu** (the owner, 2026-10-24: make the products consumer-friendly and familiar, with the developer side on OpenVibe.Services; plan of record track K). The `site` names and lines the launcher, the footer and Network's home show: | Service | Name | Line | |---|---|---| | actor | Agents | Agents that work while you are away | | bot | Robots | Drive any robot from your browser | | codes | Code | Build and fix apps with AI | | watch | Alerts | Know the moment it changes | | work | Jobs | Find work, with an agent on your side | | rent | Rentals | Every rental site, one search | | trade | Markets | Watchlists and alerts | | host | Hosting | Hosting, compared honestly | | vip | Memberships | Memberships for creators and their software | | space | Spaces | Make a space: an app on your account | | quest | Quests | Badges for what you really do | | inventory | Items | Your items, on every OpenVibe site. | | run | Compute | Run jobs on machines that can. | | zone | Storage | Private S3-compatible storage for your projects. | | ai | AI API | Many models, one API. Every answer explained. | | play | Game services | Achievements, leaderboards and cloud saves for every game. | - Chat's line is now "Chat with Vibe, friends and groups": Vibe, OpenVibe's assistant, lives at openvibe.chat/vibe (OpenVibe.Chat#139). - Service ids, domains, capabilities and events are unchanged: only what people read. - **`turn_epoch` on a device command** (`bot.device-message@1`, additive). This was the community LOOI driver's reviewers' question A2. It carries the robot's control epoch when Bot decided the command: it rises when a queue turn ends, is left or is paused by an owner takeover (OpenVibe.Bot#155), and Bot's halt for that change carries the new value. A device or plugin may refuse a command older than the newest halt's epoch. It is milliseconds on Bot's clock, so it keeps rising across Bot restarts. Older devices ignore it (Node's decoder skips unknown fields). ## 0.212.0 — 2026-10-24 - **A tab-separated body, and which duplicate wins** (OpenVibe.Food's BLS prices). An api endpoint of `sources.source@1` and `sources.source-write-request@1` may say `"format": "tsv"`: a file whose first line names the columns, read as an array of records keyed by those names (`items_path` is ""). `"duplicates": "first" | "last"` says which of two records with the same identity is kept (first, as before, by default); BLS's series files list each series oldest to newest, so `last` keeps its newest month. - **`extra_lists` on an api endpoint of a source** (plan 2026-10-22, track F2). `sources.source@1` and `sources.source-write-request@1` take, beside `extra`, up to ten list fields: each maps to a path whose array of scalars Sources keeps as up to 20 of them (a lone scalar is a list of one, a missing value an empty list). Job boards give tags and job types as arrays, which `extra` reads as null; OpenVibe.Work filters by both, so it can follow Arbeitnow through Sources without losing them. Additive: an older reader ignores the field, and `extra` is unchanged. - Catalog copy checked against each product's code once more (plan 2026-10-22, track H5; Bot, Actor and Help were fixed in 0.210.0). - **OpenVibe.Watch** promised what it does not do: - "videos, streams and cameras" as sources, "AI summaries", and keywords for YouTube video analysis and security camera alerts (the `ai` extraction and the browser rung do not exist yet); - "robots rules" on every check (Watch reads no robots.txt; it sends the site's validators, spaces requests per host and honours Retry-After). - It also left out what it does: a price, OpenVibe events and webhooks with no polling, a device on your own network checked by your computer, and actions that start an Actor agent or a coding task, all on its site. - "Next" is now browser checks and AI summaries. - **OpenVibe.Quest**'s "Next" named OpenCoins rewards (on in production), quests for OpenVibe.Games (counted: joining and publishing a world) and quests streamers and communities write, which its own FAQ rules out; the "streamer quests" keyword goes too. - **OpenVibe.Work**'s "Next" named Watch alerts and the Actor helper, both live: any search is an Atom feed Watch follows, and the agent drafts an application and asks before each step. - **OpenVibe.Food**'s "Next" named the household chat, which is live: a household opens a room on OpenVibe.Chat with Pantry, an AI member that knows the plan and every cupboard. - Pics, Download, Video, Rent, Homes, Events, Services and Zone read true against their code. ## 0.211.0 — 2026-10-23 - **`retire_after_sec` on a source** (plan 2026-10-22, track F2). `sources.source@1` and `sources.source-write-request@1` take an optional integer (an hour to a year) or null. Once a fetch of the source succeeds, an item it has not seen for that long is removed with reason `gone_from_source`: a change in the feed, so a consumer following `GET /api/v1/items` learns that the job or the listing is gone. Sources kept every item it ever saw, so OpenVibe.Work could not follow Arbeitnow through it without showing closed jobs. null, the default, keeps today's behaviour for news, prices and datasets. Additive: an older reader ignores the field. ## 0.210.0 — 2026-10-22 - **Sources categories `housing`, `food` and `jobs`** (plan 2026-10-22, track F1). `sources.source@1`, `sources.item@1`, `sources.source-write-request@1` and the four `sources.*` event payloads accept them, so the owner's top aggregators can move their own fetches into Sources: - OpenVibe.Rent's and Homes' listings and rent data; - OpenVibe.Food's recalls and food data; - OpenVibe.Work's job boards. They then share Sources' robots and terms checks, retention and the Search feed. A consumer that switched on the old nine values should treat an unknown category as "other". Nothing in production sends the new values until Sources pins this release. - Catalog copy that promised what the products do not do (plan 2026-10-22, track H5), checked against each product's code. **OpenVibe.Bot**: - Its description, FAQ and keywords named ESP32 boards, Android apps, ONVIF and RTSP cameras, ready-made bots to buy and "kits that list exactly what to buy". None ships: - OpenVibe sells no hardware, and the one curated kit (Adeept ADR036) is a parts list; - the ONVIF camera is a server-side connector the operator configures. - It now names what Bot's profiles and drivers run: the Adeept kit, ROS 2, Cozmo, LOOI, a phone in its browser, and the generic relay for anything else. - The planned-site `vision` block (robot arms, telepresence bots, a recording toggle, "flash a ready-made card") and the `plannedRepo`/`meanwhile` pointers go, as for the other live products; nothing reads them since OpenVibe.Sites retired. - **OpenVibe.Actor**'s cost answer gives its real free tier (20 tasks a day, $0.05 a task, $0.25 a day), not "limits published before launch". - **OpenVibe.Help** no longer says "live or coming": every site it covers is live. - The catalog changes are text only. ## 0.209.3 — 2026-10-21 - Codes' site text names OpenVibe Git at `git.openvibe.codes`, where it moved on 2026-10-21 (Codes#93; the old `openvibe.codes/git` redirects there), and its "Where is the code?" answer mentions the read mirrors there. Network's frame footer prints `site.what` on Codes' pages. Text only, with no schema or status change. ## 0.209.2 — 2026-10-21 - The service manifests of Reviews, Tips and VIP no longer say they are "deployed internally (loopback only), not launched publicly" behind the OpenVibe.Sites placeholder. Reviews has been public since 2026-10-09, and Tips and VIP since 2026-10-17. Network's public registry API served the old notes. Notes only, with no schema or status change. ## 0.209.1 — 2026-10-21 - The four Watch capabilities (`watch.watch.read`, `watch.watch.manage`, `watch.observation.read`, `watch.check.run`) no longer say "PLANNED (plan T18; OpenVibe.Watch is not built yet)" or that they become active once Watch implements their routes. They have been `active` since Watch launched on 2026-10-08, and openvibe.services' capability docs printed the old note beside each. Descriptions only, with no schema or status change. ## 0.209.0 — 2026-10-20 - OpenVibe.Space's site capabilities are public, so a developer app can be granted them: `space.deploy.create` (sensitive: upload, activate and roll back deploys — "an app principal added to a project as deployer can deploy from CI", which no app could do while it was first-party), `space.site.config` (headers, local redirects, the SPA fallback), `space.site.manage` (sensitive: sites and members; apps still cannot own projects) and `space.domain.manage` (sensitive: custom domains, served only after DNS TXT verification). Space judges every one by the acting principal's project role as well: an app is a principal of its own and can touch only the projects a person added it to, as deployer, maintainer or owner. Sandbox tokens stay confined to sandbox projects. ## 0.208.0 — 2026-10-20 - Coupons, Deals and Trade open what a person does for themselves to apps (all three already act for an app only as its `on_behalf_of` person and refuse sandbox tokens): - Coupons: `coupon.lookup` and `merchant.resolve` (public data), `coupon.submit` (sensitive: a code under the person's name, status unknown until reports) and `report.create` (worked or failed, one a day per code). - Deals: `offer.submit` (sensitive), `offer.update` (the person's own offers, or a new price observation), `flag.create`, `vote.set` (sensitive) and `vote.remove`, and `watch.create`, `.read` and `.delete`. - Trade: `instrument.resolve`, and the person's own `watchlist.*` and `alert.*`. - Moderation (`coupons.status.update`, `coupons.merchant.manage`, `deals.offer.moderate`, `.merge`, `.expire`), `deals.product.resolve` (which creates products) and `trade.context.read` (drafts awaiting review and OpenVibe.AI's workflow input) stay first-party. ## 0.207.0 — 2026-10-20 - Reviews and the Wiki open their reads to apps: `reviews.entity.resolve` (look up a business, product or place and read its record and history), `reviews.correction.submit` (send a correction to the editors' queue for the person), `wiki.page.read` and `wiki.search.query` (what the person may read; without a person, public pages only) are `public`. Both services already act for an app only as its `on_behalf_of` person and refuse sandbox tokens. News' `story.read` and `cluster.read` stay first-party: they are the editorial reads (drafts, flags, ingestion), and published stories need no capability. `wiki.revision.propose` stays first-party (it is OpenVibe.AI's output). ## 0.206.0 — 2026-10-20 - Blog's posts are open to apps: `blog.feed.read` and `blog.post.create`, `.read`, `.update`, `.publish`, `.unpublish`, `.schedule` and `.delete` are `public` (they were `first-party`), so Network can grant them to a developer app for the person who authorizes it. Blog already acts for an app only as its `on_behalf_of` person and refuses sandbox tokens; AI-origin drafts (`X-OV-Origin: ai`) stay first-party, since only a `svc:` principal may set it. Publishing, unpublishing, scheduling and deleting are `sensitive`. Creating a blog, its settings, members and theme stay first-party. This is the first step of the roadmap's "Every product in the SDK" for products whose API only their own site could use. ## 0.205.4 — 2026-10-20 - Live's site block leads with what it is for: the tagline is "Go live from your browser" (it was "Streams, clips and chat") and the summary says no OBS, no downloads, no follower minimum. It still described ARENA ALERT, the drama desk, with the Lolcow Index and the Clout Rankings, which The Rundown replaced on 2026-10-19; the Arena question in its FAQ is now "What is The Rundown?". The share card, the launcher and the help centre read these. ## 0.205.3 — 2026-10-19 - Host's catalog entry says what it is since the 19 October maintenance: offers from LowEndSpirit's Offers board (it named LowEndTalk and LowEndBox, whose feeds refuse the production host), benchmarks people run with YABS, and the console for your own servers on Linode, DigitalOcean, Vultr and Hetzner Cloud, with a question for each. ## 0.205.2 — 2026-10-19 - openvibe.website is a moved address (`kind: "moved"`, service `space`, linking to OpenVibe.Space): the apex redirects to openvibe.space and published sites take names under it, so the help centre no longer lists it as a site "still coming". ## 0.205.1 — 2026-10-18 - Space's catalog entry describes spaces as built (they shipped 2026-10-16; it said "next and not built yet"), and its FAQ says plainly that publishing for everyone waits on abuse handling and a designated DMCA agent. ## 0.205.0 — 2026-10-18 - `sources.source@1`, `sources.item@1`, the source write request and the `sources.item.*` / `sources.fetch.failed` payloads: a `games` category, for game store data OpenVibe.Games shows (Steam's featured lists: top sellers, new releases and specials, read from store.steampowered.com/api, which its robots.txt allows). - `quest` consumes `network.subject.merged`: account merges move a person's quests to the survivor (OpenVibe.Quest#65). ## 0.204.1 — 2026-10-18 - Catalog says what each site is today (owner: "lots of stuff across websites is still unfinished / coming soon / next steps"): every stale `launch` note is gone (Actor, Bot, Download, Events, Help, Pics, Quest, Rent, Services, Watch, Work, Website, Zone, Coupons, Host, News, OpenRestream, Reviews, Space, Tips and VIP are live), and so are the "Next" pillars (Pics, Video) and "planned"/"comes later" sentences (Services, Video, Pics, Events, admin, themes). openvibe.website describes OpenVibe.Space (where it redirects) and Zone its live S3-style storage, in the present tense. - FAQs for the 16 sites Help showed with none: Download, Events, Homes, Pics, Services, Video, Website, Zone, News, Reviews, Run, Tools, Trade, Wiki, Play and Search. Quest's FAQ: OpenCoins rewards are on (`QUEST_COINS=on`). ## 0.204.0 — 2026-10-18 - `sources.source@1`: an api endpoint may carry `timeout_ms` (1 s to 120 s), its own fetch deadline instead of the deployment's (OpenVibe.Sources#76: NYC Open Data computes the HPD per-building violation tally in 12 to 60 seconds). ## 0.203.0 — 2026-10-17 - `billing.stats.read` (`GET /api/v1/stats`, `billing.stats-result@1`): network-wide money counts with no person in them (Vibes bought with money, channel subscriptions active and started, recent windows and daily series), so a product's public stats read Billing, the money ledger, instead of tables that stopped at the cutover (OpenVibe.Live's home). ## 0.202.0 — 2026-10-17 - OpenVibe.Tips: other ways to support a creator, paid outside OpenVibe (the owner's 10-08 ask: Billing is provider agnostic; creators bring Ko-fi, PayPal.me, Patreon and other links). Optional `support_links` on `tips.profile@1` (`{ provider, label, handle, url }`, Tips builds the link) and on `tips.profile-update-request@1` (`{ provider, handle }`, at most 8). Providers: Ko-fi, PayPal.me, Patreon, Buy Me a Coffee, Cash App, Venmo, GitHub Sponsors, Liberapay, Open Collective, Streamlabs and StreamElements tip pages. ## 0.201.0 — 2026-10-17 - Subscription tiers priced by OpenVibe.Billing (the owner's VIP ask: creator memberships with tiers). New capability `billing.tier.manage` (`GET /api/v1/tiers/:streamer`, `PUT /api/v1/tiers/:streamer/:tier`) with `billing.tier-request@1` and `billing.tier-result@1`: the product where a creator sells tiers (OpenVibe.VIP) sets each tier's monthly price within Billing's bounds; Billing keeps and charges it (ADR-012). Optional `tier` on `billing.intent-request@1` (subscriptions), `billing.subscription-request@1` and `billing.entitlement@1`. Tier 1 without a price keeps the site's default subscription price, so nothing changes for anyone who does not set tiers. ## 0.200.0 — 2026-10-17 - OpenVibe.Tips and OpenVibe.VIP are live sites (`exposure.state: live`, `publicSite: service`) now that OpenVibe.Billing is the money ledger. Tips: a tip page per creator, paid messages, text-to-speech, media requests, paid sounds, goals and OBS overlays on any platform. VIP: memberships open to anyone, with perks across OpenVibe and subscriptions to people's own software (openvibe-sdk/vip). Both catalog entries say so, with FAQs, and name what comes next. - Tips: paid sounds (the owner's "paid soundboard … triggers"). A new interaction `kind: sound` appears in every Tips kind enum (interaction, public interaction, moderation view, simulation request, and the tips.interaction.ready, failed and cancelled payloads). Optional `sound` fields cover the paid and simulation requests (`{ id }`) and the interaction and public interaction (`{ id, name, url }`). The profile gets optional `sounds` (`enabled`, up to 12 `items`) and `minimums.sound`; the profile update gets `sounds_enabled` and `sound_min`. Capability `tips.sound.request` covers `POST /api/v1/sound-requests`. Everything is optional or additive, so the current Tips release still validates. ## 0.199.3 — 2026-10-16 - Chat's catalog copy says what it is now: rooms with AI members on their own models with tools, Discord-style servers with roles, invites and text and voice channels, pictures, DMs, global and stream chat (Chat#102-#109). Its tagline is "Group chats with AI, servers and voice". - Space's summary adds that spaces run their own server code (space functions, Space#62). ## 0.199.2 — 2026-10-16 - Space's catalog copy names spaces (small apps on a person's OpenVibe account: the AI builder, templates, the editor, sign-in, shared lists, private storage and AI; Space#58) ahead of static hosting, with a FAQ (`site.faq`). ## 0.199.1 — 2026-10-16 - Chat's FAQ (`site.faq` in its service manifest): one account everywhere, servers with text and voice channels and roles, AI members in rooms (OpenVibe's AI or the person's own key, with tools), pictures through OpenVibe.Pics. - Live's one-line `what` names ARENA ALERT, the drama desk on every stream. ## 0.199.0 — 2026-10-16 - `host.index_document.upserted` / `.deleted` v1 also carry **domain endings** (type `tld`, id `tld-<tld>` such as `tld-dev` or `tld-co.uk`, page /catalog/domains/<tld>): the cheapest first year, renewal and five years at any registrar, facets kind, register_usd, renew_usd. ## 0.198.2 — 2026-10-16 - FAQs for the twelve sites whose Help pages had none: Live, Blog, Codes, Community, Coupons, Deals, Games, Host, Media, Network, Inventory and the AI service (`site.faq` in their service manifests). ## 0.198.1 — 2026-10-16 - Host's catalog copy names what openvibe.host compares today: plans, the offers providers post on LowEndTalk and LowEndBox, and domain prices (first year, renewal, transfer, five years per ending) with a name check at the registry; a "Domains too" pillar; the old line about Host's own static hosting opening after a review now points to OpenVibe.Space. ## 0.198.0 — 2026-10-16 - `rent.index_document.upserted` / `.deleted` v1 also carry **OpenVibe.Homes city pages** (type `homes_city`, the same `<state>-<slug>` id, canonical on openvibe.homes): the median home value and rent, sent by Rent's sweep. ## 0.197.2 — 2026-10-16 - Copy: OpenVibe.Work's "Will an AI apply for me?" says the Actor agent helps prepare applications today (it said the agent was coming), and OpenVibe.Quest's community-quests answer names the SDK's app quests that already work. ## 0.197.1 — 2026-10-16 - Copy: OpenVibe.Rent's FAQ says where the listings come from (every major rental site, opened at your place) instead of "nothing is copied in this first version", and answers "How do I know if a rent is fair?" (Rent by city, the rent check, the Companion); its pillars add Rent by city. OpenVibe.Play's tagline and `site.what` name cloud saves. ## 0.197.0 — 2026-10-16 - `rent.index_document.upserted` / `.deleted` v1 also carry **city pages** (type `city`, id `<state>-<city slug>`, e.g. `tx-austin`): OpenVibe.Rent's Rent by city pages with the typical rents by size (Census ACS), sent by its sweep. ## 0.196.2 — 2026-10-16 - Fix: `media-hub` declares `openvibe.pics` and `rent` declares `openvibe.homes` in `domains`. Network builds its CORS allow-list from manifest domains, so both sites' shared navbars were refused (403 on /api/auth/me: signed-in people saw "Sign In"). ## 0.196.1 — 2026-10-16 - Copy: MediaHub's tagline is "Videos, pictures and your files" (it serves Video uploads, Pics and Download, not only Live's past streams), and OpenVibe.Games' is "Browser games, and worlds you build" (creator worlds since 0.193). ## 0.196.0 — 2026-10-16 - **MediaHub publishes events** (in its hyphen-free namespace `mediahub.*`, Events#91): `mediahub.video.published` v1 (a person's upload is public and ready for the first time: `{ upload, owner, title, url, published_at }`, public) and `mediahub.picture.uploaded` v1 (a signed-in person's picture on OpenVibe.Pics: `{ picture, owner, uploaded_at }`, internal, without the link or title since pictures are unlisted). MediaHub's eventsProduced; Quest consumes both for its "Publish a video" and "Share a picture" quests. ## 0.195.2 — 2026-10-16 - Copy: OpenVibe.Pics' pillars name resize and convert ("Any size, any format") and alt text, and its Next is AI image generation; OpenVibe.Video's AI edit names the AI cut and its playlists hold uploads; MediaHub's `site.what` follows. ## 0.195.1 — 2026-10-16 - Copy: OpenVibe.Download says what is live (a 25 GB drive, public or signed-in share links, folder backups with `openvibe-download`) instead of "will be" and "no anonymous downloads"; MediaHub's `site.what` names Download's backups and Video's uploads and playlists. ## 0.195.0 — 2026-10-15 - **Search documents for creator worlds:** `games.index_document.upserted` and `.deleted` v1 (type `world`, `/worlds/<id>`, id `w` + ten base-36 characters), `search.index-document@1` with the owner, type and id fixed, plus the tombstone. A published world is a document; unpublished, taken down or deleted, a tombstone. Games' eventsProduced; fixtures (valid document, tombstone; refused: bad id, other owner, tombstone as an upsert, other type, a full document as a tombstone). ## 0.194.0 — 2026-10-15 - **`games.world.published`** (OpenVibe.Games creator worlds): a player published one of their worlds for the first time `{ world, owner, title, url, published_at }` (public). Games' eventsProduced, Quest's eventsConsumed (the "Publish a creator world" quest). Games' `site.what` says players build and publish their own worlds. ## 0.193.2 — 2026-10-15 - Copy: Quest's `site.what` says developer apps add quests on their own events (Quest#55, SDK 0.47.0). ## 0.193.1 — 2026-10-15 - `generated/openapi/quest.json`: Quest's routes now that it declares a capability (left out of the 0.193.0 commit). ## 0.193.0 — 2026-10-15 - **App quests** (the owner's "moddable in the SDK" for Quest): capability `quest.app_quest.manage` (owner quest, public, project-scoped) and `quest.app-quest-request@1` / `quest.app-quest@1`. A developer app defines quests on its own project's events (`app.<project_key>.<name>`, the actor a person); OpenVibe.Quest counts production events only, keeps at most 20 per project, and rewards a badge named for the app — never OpenCoins. ## 0.192.0 — 2026-10-15 - **`bot.robot.online` / `bot.robot.offline`** gain an optional `owner` (the robot owner's `usr_` subject; absent from older events): OpenVibe.Quest credits the owner's "Bring a robot online" quest from it (the audit's device and robot quest type). - **Quest consumes** `blog.post.published`, `deals.offer.created`, `deals.vote.changed`, `run.job.succeeded`, `inventory.item.equipped` and `bot.robot.online` (Quest#53 and the robot quest): quests on Blog, Deals, Run, Inventory and Bot. ## 0.191.1 — 2026-10-15 - **Copy** (copy only): Deals' site names its crowdfunding discovery (Deals#62: one search across Kickstarter, Indiegogo, GoFundMe and more, beside Open Collective campaigns) and drops its `launch` (DealNews is on). Food gains the "A recipe library" pillar (Food#55: budget recipes written by AI from the food list, costed by Food). ## 0.191.0 — 2026-10-15 - Sources category `crowdfunding` (`sources.source@1`, `sources.item@1`, `sources.source-write-request@1` and the `sources.item.created|updated|removed` and `sources.fetch.failed` payloads): campaigns and collectives raising money in the open, read by OpenVibe.Deals' crowdfunding discovery (the owner's crowdfunding aggregator ask). - Sources `api` endpoints may set `request_content_type` (`application/json` only): sent as the Content-Type of the endpoint's GET. Open Collective's public GraphQL API answers a GET only with it (its CSRF guard). ## 0.190.1 — 2026-10-15 - **Copy for what shipped on 2026-10-15** (copy only): OpenVibe.Homes is live (served by OpenVibe.Rent, Rent#58): every for-sale site at once, foreclosures, land and commercial, county records, FHFA prices and a mortgage calculator; its `launch` and `noRepo` are gone. OpenVibe.Video uploads, records in the browser and makes AI captions, chapters and a summary (MediaHub#67-#71). Trade covers every company on SEC's ticker list with reported numbers and cited AI briefs (Trade#68-#70; `launch` gone). Food fills the cupboard by barcode, shelf photo or receipt (Food#53-#54; `launch` gone). Blog's `site.what` names the AI desks (Blog#57); Codes' names OpenVibe Git at openvibe.codes/git (Codes#85-#86). ## 0.190.0 — 2026-10-15 - **`live.setup.task_completed`** (owner live, internal; Live produces, Quest consumes): a streamer finished one of the 18 tasks of Live's go-live setup hub for the first time. Payload `{ task, user, title, group, completed_at, done, total }`, `task` one of the hub's ids (slot, method, restream, golive, profile, offline, emote, sound, goal, powerchat, panels, share, mods, requests, moderation, robot, aibot, paste). The owner's Q16: Live's setup steps become quests. ## 0.189.1 — 2026-10-15 - **Food compares delivery** (Food#52): its product description, pillars and `site.what` say one search opens every delivery app and meal kits are side by side; the household chat is named as next. - **Rent's board and the Companion** are built (Rent#55, Extensions v0.2.0): the product description and pillars say so instead of naming them as next. Copy only. ## 0.189.0 — 2026-10-15 - **`food.household.read`** (owner food, internal, granted to Chat): a household's pantry and plan as plain text for the AI member of its OpenVibe.Chat room, `GET /internal/households/:planId/chat-context` → `food.household-chat-context@1` `{ text, source, updated_at }` (new contract, with fixtures). The owner's Q15: a Food household's group chat with an AI member that knows what is in the cupboard. Chat's AI members (Chat#102) read it with Chat's own service token; Network grants it (Network follows); Food serves it (Food follows). ## 0.188.3 — 2026-10-15 - **OpenVibe.Pics is live** on openvibe.pics (MediaHub#64): the product's tagline, description and pillars, and media-hub's `notes` and `site.what`, say what it is now: signed-in picture uploads re-encoded without metadata, unlisted share and embed links, albums, reports and takedowns. Signed-out uploads and AI image tools are named as next. The `pics.*` capabilities stay planned (no app may call the API yet). Copy only. ## 0.188.2 — 2026-10-15 - **Rent is the rental aggregator** the owner asked for. Its `site.tagline` and `site.what`, and the product's tagline, description and pillars, now say: one search opens Craigslist, Facebook Marketplace, Zillow, Apartments.com, Zumper and more, next to HUD affordable housing and listings posted here (Rent#53). "Nothing scraped" is gone. Copy only. ## 0.188.1 — 2026-10-15 - **Quest's site text:** quests pay badges and OpenCoins. OpenCoins have been credited since `QUEST_COINS` was switched on, but the `site.what` and the product description still said "once they are switched on" (Network's front door and Help read them). Copy only; no schema or capability changes. ## 0.188.0 — 2026-10-15 - **`coupons.watch.matched` v1:** a person watching a merchant on OpenVibe.Coupons is told when a code for it is publicly listed (created published, or approved from review). One event per (watcher, coupon), never for the code's submitter, inside the listing's transaction. Coupons produces it, and OpenVibe.Network consumes it into an inbox notification linking the coupon page, as for `deals.watch.matched`. The payload carries no code text. Coupons' watches had been recorded but never delivered. ## 0.187.0 — 2026-10-15 - **openvibe.video: playlists are built** (MediaHub's `/playlists`, its API and "Add to playlist" on every watch page; MediaHub#55 puts Playlists in the navbar). The pillar list gains Playlists, and "Next" names only uploads and publishing to other platforms. ## 0.186.0 — 2026-10-15 - **More pillars say what is built** (the 0.184.0 audit, finished): openvibe.download has no AI search or summaries and no sync (its drive, share links, sign-in rule and reports instead); openvibe.quest has no quest creation for people (one log across eight sites, real activity only, badges, games); openvibe.rent does not gather other sites' listings or summarise them with AI (posted by people, HUD affordable housing, alerts through Watch, your account); openvibe.help has no anonymous or email inbox and no project widget (answers from the manifests, one search, tickets, every site); openvibe.zone's keys scope zones, not people, and zones do not back streams (keys you scope, any S3 tool); openvibe.actor calls its models directly, not through OpenVibe.AI, and there is no Website yet. ## 0.185.0 — 2026-10-15 - **openvibe.food: "Plan together" is a pillar again, because it is built** (Food#39). A saved meal plan is shared with a household by a link shown once (only its hash is kept), and everyone in it ticks the shopping list off for everyone. Delivery price comparison stays out: the delivery apps publish no open prices. ## 0.184.0 — 2026-10-15 - **The catalog says what each site does today.** OpenVibe.Help shows every site's pillars as "What you can do". Several pillars promised features that are not built; they now repeat what each site's own front page lists as working. - **openvibe.food:** "Compare delivery", "Kits & boxes" and "Plan together" are not built. The pillars are now its food near you, meal planner, food list and cupboard recipes. The prices FAQ no longer promises live delivery prices, and `keywords` and `launch` follow suit. - **deals:** prices as of a time, votes, compare across stores, watches and saved searches. There are no AI summaries. - **news:** cited paragraphs, revisions on the record, source checks, discussion. There are no "many angles" or framing controls. - **coupons:** shop matching, stated restrictions, honest expiry, reports from people. There is no browser helper. - **trade:** find an instrument, values with their times, filings, watchlists and alert rules. - **reviews:** sourced signals, a checkable aggregate, history, editor summaries. - **openvibe.pics is not a live domain of media-hub.** It is parked: MediaHub answers its host with a 302 to openvibe.network. Listing it made Help call Pics "live". Its product manifest stays its catalog home. ## 0.183.0 — 2026-10-15 - **Ask Actor from chat** (plan T17, "Reach"). OpenVibe.Actor consumes `chat.message.created` and calls OpenVibe.Chat's service API with `chat.message.send`. - **What it answers:** a signed-in person's `@actor <task>` in the global chat or their own channel becomes their task. - **Public tools only:** the task never uses the person's computers, robots or code. - **The reply** is posted back in the same room, answering the message. - Guests, relayed external chatters and other people's channels are ignored. - The service manifest's `eventsConsumed` and notes say so. ## 0.182.0 — 2026-10-15 - **The live screen** (plan T14, OpenVibe Link v0.12.0): `bot.device-message@1` commands take the kind `screen_stream`. Its value is `{ action: 'start', whip_url, publish_key, seconds?, fps? }` or `{ action: 'stop' }`. It shows the machine's screen live to its owner on the machine's own OpenRestream stream: playback private, no recording, never mirrored to Live. - Bot alone fills in the WHIP URL and key. The kind is not in `bot.command@1`, so no operator, agent or service can send it or choose where the screen goes. - `bot.computer.control` says so too. - Devices: `deadline_ms` is the reply window of `screen_stream`, as of the other computer kinds. ## 0.181.0 — 2026-10-15 - **OpenVibe.Sites is retired** (plan T11, 2026-10-14). The service manifest's `status` and `exposure.state` are `retired`, with the reason in the note, and its repository record is no longer deployed. `scripts/docs-currency.js` skips a retired service whether or not an old checkout is around, and counts it apart (`44/44 current, 1 retired skipped`). - **The estate covers every repository again** (`docs/ESTATE.md`, `manifests/repositories/`): 50, regenerated from current clones. It adds the ten built since the last refresh (Actor, Food, Help, Inventory, MediaHub, Play, Quest, Rent, Work, Zone), and the other 37 records now carry their current pins. `scripts/estate.js` reads symlinked checkouts, and a retired service's record is never "deployed". - The 0.180.0 Sources descriptions named the implementing pull request as Sources#49; it is Sources#51. ## 0.180.0 — 2026-10-15 - **Sources api endpoints read catalogs that keep plans, hardware and prices apart** (Sources#51, for OVHcloud's public VPS catalog). `sources.source@1` and `sources.source-write-request@1`: - A path segment may carry a selector, `key[field=value,…]` (one to four conditions). It steps into the first element of the array `key` whose fields equal the values, compared as text. A value is literal text or `$rec.<path>`, read from the record. This is documented on `extra` and `where`; the field types are unchanged. - A `where` expectation may be `{ "pattern": "<regular expression, at most 200 characters>" }`, which the value must match as text. - `max_bytes` (an integer from 1 KiB to 32 MiB) sets one endpoint's body cap. - Fixtures: a valid OVHcloud-shaped source with a selector, a pattern and `max_bytes`; an invalid pattern with an extra key; an invalid `max_bytes` over the cap. ## 0.179.0 — 2026-10-15 - **Watch uses project secrets** (ADR-056). - A source credential (`auth`) may name `secret_ref` instead of `env`: a project secret granted to `watch` for `reveal` to the source URL's origin, answered by OpenVibe.Network at fetch time. - A webhook action may name `secret_ref` instead of `secret_env`: a project secret granted to `watch` for `sign`, so Network signs each delivery and Watch never holds the value. Exactly one of the two is required. - `watch.watch-request@1` takes `project_env` (sandbox or production; production by default when the watch names a project), and `watch.watch@1` shows it. - Fixtures: a watch with a project webhook secret, and an invalid webhook action with neither secret. ## 0.178.0 — 2026-10-15 - **Actor webhooks name a project secret** (ADR-044 §7, ADR-056). `actor.task-create-request@1` webhooks take `secret_ref`, the lower-case name of a project secret granted to `actor` for `sign`. OpenVibe.Network signs each delivery in the same headers, so Actor never holds the value, and no `webhook_secret` is minted when every webhook names one. A task outside a project is refused 422 `actor.webhook.secret_unavailable`. - **`platform.task@1`** `secret_ref` accepts the ADR-056 lower-case names as well as the earlier upper-case environment-variable references. - Fixtures: a valid task webhook with `secret_ref`, and an invalid one with spaces in the name. ## 0.177.0 — 2026-10-15 - **Project secrets (ADR-056).** OpenVibe.Network keeps secrets per project and environment, written once and used by name: no API returns a value to a person. - `network.project-secret@1`: what members and staff see (name, environment, version, a keyed fingerprint, grants, who set it and when). - `network.secret.use` (first-party, `POST /internal/secrets/use`): a service the project granted a secret to either signs a message with it (`sign`, HMAC-SHA256; the value never leaves Network) or receives the value for an https origin the grant lists (`reveal`). `network.secret-use-request@1` and `network.secret-use-result@1` refuse a sign without a message, a reveal without an origin, and either one mixed with the other's field. - Listed in Network's manifest. Fixtures: valid signing and reveal grants, sign and reveal requests and results; invalid a secret with its value, an upper-case name, an http origin, and mixed or missing fields. ## 0.176.1 — 2026-10-15 - **`media.object.s3`** lists CopyObject and UploadPartCopy (Media#113): server-side copies within the key's environment, COPY or REPLACE metadata, the copy-source conditions, part byte ranges, and the same commit path. A `versionId` source still answers NotImplemented. ADR-031's "what v1 leaves out" says the same. ## 0.176.0 — 2026-10-15 - **Person-owned resources can be controlled** (ADR-048 amendment, step 8 phase 2, the control half). `common.owned-resource-control-request@1` names a robot, room, restream definition or paste by its index kind and id and its owner (a person). It goes to the same `POST /api/v1/resources/control`. `contracts.resources.checkControlRequest` takes either form (`checkOwnedControlRequest` for this one) and refuses a person acting on someone else's resource. - **Four control capabilities, active:** - `bot.resource.control`: stop and resume a robot's e-stop; - `chat.resource.control`: archive a room; - `openre.resource.control`: suspend and resume a restream definition; - `community.resource.control`: delete a paste. Each is listed in its service's manifest. - **Fixtures:** valid `bot-robot-stop` and `community-paste-delete-dry-run`; invalid `with-project`, `agent-owner` and `create`. ## 0.175.0 — 2026-10-15 - **`media.usage.recorded`** (ADR-031 section 8): Media's per-zone hourly rollups (`common.usage-recorded@1`). Each one names the zone twice: `resource` is its OVRN and `dimension` its id in lowercase. The series: - stored bytes (`media.object.upload`, `byte_hours`); - writes (`media.object.upload`, `media.object.delete`) and lists (`media.object.list`), each in `requests`; - reads (`media.object.read`), in `requests` and `egress_bytes`. Media produces it. Network (the project's usage) and Zone (its usage view) consume it. - **`media.object.s3`** lists multipart uploads (Media#109) and their routes. Copy and part copy still answer NotImplemented. - **ADR-031:** - the interim `s3.openvibe.network` host behind Cloudflare, where a HEAD forwarded as GET is served (Media#110); - what v1 leaves out, now that multipart and the rollups exist. ## 0.174.0 — 2026-10-14 - **`browser`, a computer kind** (plan T14/T17 next step; OpenVibe Link's computer plugin 0.5.0). `bot.command@1` takes `{ action, url, selector, text, key, max_width }`: - `open` an http(s) `url`; - `click` a CSS `selector` or the button or link showing `text`; - `fill` a field (`selector`, `text`); - `press` a named key; - `back`, `read`, `screenshot` and `close`. The browser is the agent's own, in a profile of its own on the person's machine. `read`, `screenshot` and `close` run at every level; the rest are refused at observe and approved one by one at ask. Below level full, `open` refuses an address on the machine's own network. `bot.computer.control` and the device frame list `browser`. - **Fixtures:** valid `browser-open` and `browser-fill`; invalid `browser-open-file`. ## 0.173.0 — 2026-10-14 - **OpenVibe.Zone is live, and so is Media's S3 surface** (ADR-031 and its zone amendment). Zone owns named object zones per project environment at https://openvibe.zone (port 5050), provisioning each change in Media by revision. Media serves the zones as S3 buckets and keeps the objects. - Zone's `zone.*` contracts and capabilities are now active, and two more are added: `zone.resource.read` and `zone.resource.control` (delete, for Services). - **Media has three new capabilities:** - `media.object.s3`: the S3 door, reached with an access key; - `media.access_key.manage`: issue, list and revoke keys, for a project admin; - `media.zone.provision`: Zone only. - **Access-key schemas:** `media.access-key@1`, `media.access-key-issue-request@1`, `media.access-key-issued@1` (the only answer with the secret) and `media.access-key-list@1`. - **ADR-031 amendment 2026-10-14:** - Media issues the keys, not Codes: the verifier must hold the secret, and Codes became the coding harness. - The S3 surface is served by Host. - Zone serves on its apex until api.openvibe.zone has DNS. ## 0.172.2 — 2026-10-14 - **Who consumes `network.subject.merged`, written down.** Live, Chat, Community, Media and Games have folded merged accounts since ADR-029 (2026-09-27) and Play since 0.2.0, but none of their manifests listed the topic in `eventsConsumed`; all six do now. No change in behaviour. ## 0.172.1 — 2026-10-14 - **Play's site has its own icon.** `play`'s site block named the `games` icon, and the Network frame keys a site's navbar entry by its icon, so Play showed as a second Games. It names `play` (openvibe-shared 4.1.4's podium and star). ## 0.172.0 — 2026-10-14 - **`play`, a new service** (OpenVibe.Games ADR-0007 decision 10): OpenVibe.Play at play.openvibe.network (port 5040), the platform's game services. A title is registered by its own principal, which alone writes to it; it holds achievements and leaderboards (best or sum, this week and all time), idempotent score and unlock posts, and guest adoption. Boards and titles are public reads. People's rows are in the account export and erasure (consumes `network.account.export_requested` and `network.account.deleted`). Site block: Play, position 35 (the games icon). - Capabilities (active, public): **`play.title.manage`**, **`play.result.write`**, **`play.player.read`**. - Additive. ## 0.171.0 — 2026-10-14 - **`input`, a computer kind** (plan T14/T17, "keyboard and mouse input"): `bot.command@1` takes `{ action, x, y, width, height, dy, text, keys }`. move, click, double_click and right_click go to a point, scroll by `dy` clicks, type text into whatever has focus, or press one key or chord (`keys`: letters, digits, `+`, `-` and `_` only, for example `ctrl+s`). Coordinates are in the space of the screen picture the caller last saw (`width` and `height`, the `screen` kind's answer); the Link scales them to the real screen. The machine's level decides: refused at observe, approved one by one at ask. `bot.computer.control` lists it for a person's agent; the device frame lists it. - Fixtures: valid `input-click` and `input-key`, invalid `input-key-shell`. Additive. ## 0.170.0 — 2026-10-14 - **`space.resource.control`** (active, first-party, sensitive): OpenVibe.Space's control API (ADR-048, plan T13 step 10). `POST /api/v1/resources/control` takes `delete` for a `space.site`, `space.deploy` or `space.domain`, exactly as the API's DELETE routes (a site's deploys and domains go with it; the deploy being served, a default domain and anything taken down are refused with Space's own reason). Space decides by its own project roles (maintainer or owner). Dry runs; idempotent per (caller, project, key). The `space` manifest lists it. Additive. ## 0.169.0 — 2026-10-14 - **`actor.resource.control`** (active, first-party, sensitive): OpenVibe.Actor's control API (ADR-048, plan T13 step 10). `POST /api/v1/resources/control` takes `stop` for a person's `actor.task` (cancel: a queued task ends at once, a running one is aborted and ends cancelled). The `actor` manifest lists it. - **`media.resource.control`** (active, first-party, sensitive): OpenVibe.Media's control API. `delete` soft-deletes a person's `media.object` exactly as `DELETE /api/v1/objects/:id` does (restorable until its retention period ends; held and legacy-managed objects are refused). The `media` manifest lists it. - Both: only the resource's owner, inside the OVRN's project; dry runs; idempotent per (caller, project, key). Additive. ## 0.168.0 — 2026-10-14 - **`events.resource.control`** (active, first-party, sensitive): OpenVibe.Events' control API (ADR-048, plan T13 step 10). `POST /api/v1/resources/control` takes a `common.resource-control-request@1` for a project's subscription from a first-party service acting for its consumer. The actions are suspend (disable) and resume (enable); there are dry runs, and requests are idempotent per (caller, project, key). It is the second authority behind OpenVibe.Services' control route. The `events` manifest lists it. - Additive. ## 0.167.0 — 2026-10-14 - **`probe`, a read-only computer kind** (plan T18 step 7, the `node` carrier): `bot.command@1` takes `{ target, timeout_s }`. The target is `http(s)://…`, one GET from the machine with no redirect followed, or `tcp://host:port`, which checks that it accepts a connection. Targets are on the machine's own network only: private, loopback or link-local addresses. The device `command` frame lists it. - **`bot.computer.control`**: OpenVibe.Watch may send only `probe`, for owners who turned on "Let my watches check things from this computer". `watch.watch@1`'s `node` source documents `node_id` (the machine) and `probe` (that value). - Additive. Fixtures: valid `probe`; invalid `probe-ftp`. ## 0.166.0 — 2026-10-14 - **Computer control (plan T14/T17, OpenVibe Link's `computer` plugin, Node#41).** `bot.command@1` takes the kinds `exec` (argv, cwd, timeout_s, stdin), `fs` (op list|stat|read|write|mkdir, path, offset, length, content_b64, mode) and `screen` (max_width), each with its value's shape. `bot.command-result@1` and the device `ack` of `bot.device-message@1` gain `data`: the machine's answer. The device `command` frame lists button, point and the computer kinds. - **`bot.computer.control`** (active, first-party, sensitive): an agent service uses a person's own machine through `POST /api/v1/robots/:id/commands`. It works only for the machine's owner, and only when the owner turned it on for their agent. The owner's control level on the machine decides what may happen. The `bot` manifest lists it. - Additive. Fixtures: valid `exec`, `fs-read`, `screen` and an ack with data; invalid `exec-shell-line`, `fs-bad-op`. ## 0.165.0 — 2026-10-14 - **`watch.resource.control`** (active, first-party, sensitive): OpenVibe.Watch's control API (ADR-048, plan T13 step 10). `POST /api/v1/resources/control` takes a `common.resource-control-request@1` for a watch from a first-party service acting for the watch's owner: suspend, resume, delete; dry runs; idempotent per (caller, project, key). It is the first authority behind OpenVibe.Services' control route. The `watch` manifest lists it. - Additive. ## 0.164.0 — 2026-10-14 - **Planned (plan T13):** `services.recipe.run` (POST /api/v1/recipes/:id/run, sensitive), `services.usage.read` (GET /api/v1/usage) and `services.cost.read` (GET /api/v1/costs) are project-scoped, first-party capabilities of OpenVibe.Services. Services does not serve them yet. The `services` manifest lists them. - Additive (Contracts#114, re-applied on 0.163). ## 0.163.0 — 2026-10-14 - **`sources.source@1`: `extract` on an rss/atom endpoint** (plan T9, Sources#45). It reads the facts an item states in its own title or summary by the source's patterns: `kind`, `require`, and `fields` (`from` title|summary, `pattern` with one group, `as: date`). An item that yields every required field is stored as that kind with those fields. The first user is `dealnews-coupon-codes`: the coupon code, the shop and the stated end date of DealNews deals, read by OpenVibe.Coupons. - Additive. Fixtures: valid `rss-extract`; invalid `extract-from-body`, `extract-no-require`. ## 0.162.0 — 2026-10-14 - **`codes.task.start`, `codes.task.read`, `codes.task.cancel`** (plan T16/T17): a first-party service starts a coding task for a person on OpenVibe.Codes (`POST /api/v1/tasks`, the person in `X-OV-Subject`), reads it and cancels it. - Codes runs the task on that person's own machine through OpenVibe.Run, in the project of their latest openvibe.codes/run task. Without one: 409 `codes.no_project`. - Only the service that started a task, for the same person, sees it. - OpenVibe.Actor is the first caller: its `codes` agent routes coding work there instead of refusing it. - New contracts: `codes.task-start-request@1`, `codes.task-start-result@1`, `codes.task@1` (state, page URL, and once ended: ok, summary, files, patch, push, cost on the person's own keys, duration). - Additive. `codes` declares the three capabilities in its service manifest. ## 0.161.0 — 2026-10-14 - **`platform.resource-offer@1`: `owner` for a user-owned offer** (plan T14, ADR-046). A person's own Node is offered with `owner` (`usr_…`), and such an offer is eligible only for that person's own workloads: a placer must never put a user-owned offer without an owner, or with another owner, on anyone's work, and a public projection leaves user-owned offers out. Until now nothing tied a user-owned offer to its owner, so a job naming `user-owned` could have been placed on a stranger's machine (no such offer had ever been published). Additive: `owner` is optional in the schema, and the rule is enforced by the placer (OpenVibe.Run) and the dispatcher (OpenVibe.Bot). The two user-owned fixtures carry an owner; a new invalid fixture has a malformed one. ## 0.160.0 — 2026-10-14 - **`bot.robot-profile@1`: `mapping.driver` may be `plugin`** (plan T15 "community-submitted profiles and drivers"): the robot is driven by a third-party OpenVibe Link plugin, named in `mapping.plugin`, that its owner installs on their Node. A community driver no longer needs a contract release of its own (as `looi` did); OpenVibe.Bot reviews and signs community profiles before robots can use them. New valid fixture `community-plugin.json`. Additive only: every existing profile stays valid. ## 0.159.0 — 2026-10-14 - **Contracts 0.159.0: sandbox-provider job contracts (plan T14 R4b, ADR-034; planned, internal).** What OpenVibe.Run sends to and reads from an external sandbox provider such as E2B or Fly Machines, the same shape as the browser-provider set: `sandbox-provider.job-create-request@1` (`id`, `class` code or linux, `args`, `inputs` pinned by sha256, `limits`, `egress`; no project, subject, requester or credential), `sandbox-provider.job-state@1` (`state`, `started_ms`, `finished_ms`, `wall_ms` as the provider's claim only, `exit_reason`, `exit_code`, `result`, `stdout` in Bot's ring shape) and `sandbox-provider.job-create-result@1` (`{ job, created }`). New capability `sandbox_provider.job.dispatch` (Run's own outbound call, `planned`, `implementedBy` empty). Additive only. ## 0.158.0 — 2026-10-14 - **`live.chat_mirror.write` is retired.** The Live read mirror is gone: Chat stopped its capture triggers (Chat migration 0006) and drops its table (0009), Live keeps no chat tables and reads through Chat's internal read API, and OpenVibe.Network revokes the grant at boot. The capability manifest stays loadable with status `retired`; `manifests/services/live.json` no longer lists it. No schema change. ## 0.157.0 — 2026-10-14 - **`bot.robot-profile@1`: `mapping.driver` may be `phone`** (plan T15 "an Android phone as the robot's brain"). A phone joins OpenVibe.Bot as a device from its own browser, with nothing to install: it pairs with a one-time code (or a QR code), publishes its camera over WHIP, speaks, shows a face or text, and reports its battery and tilt. The driver runs in the page (OpenVibe.Bot `public/phone-device.js`), not as a Link plugin. Additive only: every existing profile stays valid. ## 0.156.0 — 2026-10-13 - **OpenVibe.AI's catalog copy says the public API is live.** In `manifests/products/ai.openvibe.services.json`, the description says the public API and developer console are live, with OpenVibe.Live and OpenVibe.Network calling it too. `launch` and `relationships.meanwhile` are removed because the product is live and nothing blocks it. Copy only; no schema, capability or event changes. OpenVibe.Sites mirrors this manifest through `scripts/sync-catalog.js`, so it needs a pin bump and a resync. ## 0.155.0 — 2026-10-13 - **Contracts 0.155.0: browser-provider job contracts (plan T14 R4a, ADR-034; planned, internal).** What OpenVibe.Run sends to and reads from an external managed-browser provider: `browser-provider.job-create-request@1` (`id`, `class` browser, `args`, `inputs` pinned by sha256, `limits`, `egress`; no project, subject, requester or credential, so the provider never learns who pays), `browser-provider.job-state@1` (`state`, `started_ms`, `finished_ms`, `wall_ms` as the provider's claim only, `exit_reason`, `exit_code`, `result`, `stdout` in Bot's ring shape; no node, project or class field) and `browser-provider.job-create-result@1` (`{ job, created }`, 201 or 200 on an idempotent repeat). New capability `browser_provider.job.dispatch` (Run's own outbound call, `planned`, `implementedBy` empty). Additive only. ## 0.154.0 — 2026-10-13 - **Contracts 0.154.0: `services.resource.control` capability (ADR-048 control operations)** (first-party, active): `POST /api/v1/resources/control` on Services takes `common.resource-control-request@1` and answers `common.resource-control-result@1`. Services forwards the operation to the authority that owns the OVRN through `openvibe-sdk/resources`, is idempotent on `idempotency_key`, answers `unsupported` for an authority with no control endpoint, and refuses a sensitive action with `confirmation_required` until its owner confirms (WS-Z2). Permission `resources:control`; listed in Services' manifest capabilities. Additive only. ## 0.153.0 — 2026-10-12 - **Project delegation (ADR-003 amendment 2026-10-12).** A first-party service acting for a person (`X-OV-Subject`) may name one project with `X-OV-Project: prj_…`; the receiver checks with Network that the person is an admin or owner of the unarchived project (cache at most 60 s), records project, person and `via`, and shows the service only what it started. Apps, mods and agents never may (403 `project.not_delegated`). - **`network.project.member.read`** (first-party, active): `GET /internal/projects/:project_id/members/:subject` on Network answers `network.project-member-result@1` (`project_id`, `subject`, `role`, `archived`, `added_at`); a non-member and an unknown project are the same 404 `project.member_not_found`. New schema with fixtures. - **Watch:** `watch.watch-request@1` takes `project_id` (checked at save with that read). The `run-job` action takes `run.job-create-request@1` fields (`class`, `artifact`, `args`, `limits`, `egress`, `ttl_ms`) and `firing_arg`; the draft's `runtime`/`command`/`workload` stay in the schema for compatibility but Watch refuses them. - `run.job-create-request@1`'s description states the delegated form. Additive only (compat vs 0.152.0: no breaking changes). ## 0.152.0 — 2026-10-12 - **`video.playlist.read` and `video.playlist.manage` are active.** openvibe.video's playlist API (MediaHub#33) takes a person's Network token, an app/agent/service token with one of these, or no token for a public or unlisted playlist by id and a person's public list (`GET /api/v1/video/users/:username/playlists`). An app owns playlists under its own requester and project and never touches a person's. - New schemas (owner media-hub): `video.playlist@1`, `video.playlist-read-request@1`, `video.playlist-read-result@1`, `video.playlist-manage-request@1` and `video.playlist-manage-result@1`, with fixtures; the two capabilities name them. Additive only (compat vs 0.151.0: no breaking changes). ## 0.151.0 — 2026-10-12 - The media-hub site is named **Video** (tagline "Past streams, clips and your drive"): its public origin is openvibe.video, which serves the network's watch pages since MediaHub#28, and the network directory showed it as "Media Hub". Its id, icon and position are unchanged; openvibe.download stays in its description. No contract shape changes. ## 0.150.0 — 2026-10-12 - OpenVibe.Host removed its copy of the static hosting engine (Host#84); OpenVibe.Space serves it as `space.*` (0.149.0). The five engine capabilities (`host.site.manage`, `host.deploy.create`, `host.domain.manage`, `host.site.config`, `host.resource.read`) are retired with no routes, and the fourteen engine schemas (project, site, deploy, domain, site-config, their request/result pairs and the `host.deploy.activated` payload) are retired but stay loadable, each deprecated in favour of its `space.*` twin until 1.0.0. The host manifest keeps `host.catalog.manage`, `host.index_document.*` and `host.release.published`; Host answers the old routes with 410 `host.moved_to_space`. - OpenVibe.Video's first slice is live on openvibe.video (MediaHub#28): the product text and the media-hub manifest say what it serves (watch pages for Live's recordings and clips, AI chapters and captions) and what is not built. `video.*` stays planned. ## 0.149.0 — 2026-10-12 - The static hosting engine moved from OpenVibe.Host to OpenVibe.Space (plan T20 step 2), so its contracts are now `space.*`: capabilities `space.site.manage`, `space.deploy.create`, `space.domain.manage`, `space.site.config` and `space.resource.read`; schemas `space.project`, `space.site`, `space.deploy`, `space.domain`, `space.site-config` and their request/result pairs; events `space.deploy.created`, `space.deploy.activated`, `space.deploy.failed` and `space.domain.verified`, each with a payload schema (Host only ever had one for `deploy.activated`). Tenant sites are `<site>.openvibe.website`; a custom domain is verified by `_openvibe-space.<hostname>`. The space manifest owns them and its catalog text says what is built (staff-only until the public launch). - `space.site` uses the site id pattern the engine has always issued (`sit_`); `host.site` said `site_`. - The `host.*` hosting ids stay until OpenVibe.Host has removed the engine; a later release retires them. ## 0.148.0 — 2026-10-12 - Catalog text (`site.what`) for rent, food, work and games names what shipped today: Rent's affordable housing from HUD open data, Food's recipe ideas, Work's application tracker with an Actor agent, Games' Salvage Rush and leaderboards. No contract shape changes. ## 0.147.0 — 2026-10-12 - `work.application.manage` (owner work): a person's tracked job applications on OpenVibe.Work, for apps and agents (`work.application@1`, `work.application-write-request@1`, `work.application-result@1`). Asking the person's OpenVibe.Actor agent about one is the person's own act and takes no capability. The work manifest lists it. - `food.recipes.suggest` (owner food): recipe ideas from the caller's pantry through OpenVibe.AI's `food.suggest_recipes`, priced from Food's own food list (`food.recipe-suggest-request@1`, `food.recipe-suggestion@1`, `food.recipe-result@1`). The food manifest lists it. ## 0.146.0 — 2026-10-12 - `sources.source@1` and `sources.source-write-request@1`: an `api` endpoint may declare `where` (up to 10 path → value conditions, a value or a list of values; records that fail are left out before `max_items` counts). Paths may read the response from its root (`$root.<path>`, with a `{$key}` segment for the record's map key) and step into an object's single child (`*`), matching OpenVibe.Sources' adapter (Sources#39). This lets one AWS price list file give each instance bundle its own price. ## 0.145.0 — 2026-10-11 - `host.index_document.upserted` and `host.index_document.deleted` (owner host, active): the hosting catalog's plan and provider pages in OpenVibe.Search (types `plan`, `provider`; facets provider, kind, currency), sent through openvibe-publishing/search-feed. The host manifest produces them. ## 0.144.0 — 2026-10-11 - `sources.source@1` and `sources.source-write-request@1`: an `api` endpoint may declare `items_shape` (`array`, the default, or `map`: records keyed by id, the key readable as `$key`), matching OpenVibe.Sources' adapter (Sources#37). ## 0.143.0 — 2026-10-11 - `watch.watch@1` (and the watch write schemas that list actions): an action of kind `actor-agent` runs one of the watch owner's personal agents on OpenVibe.Actor (`actor.agent.run`) with the firing as its input after an optional note. ## 0.142.0 — 2026-10-11 - Games milestone M3 (scripted mods): `games.script.run` and the per-mod script bindings `games.players.read`, `games.player.message`, `games.world.emit` and `games.storage.kv` (active). A binding is absent from a script unless its install was granted it; grants travel in the def-set digest. Their schemas: `games.script-call@1`, `games.script-call-result@1`, `games.script-players@1`, `games.script-message@1`, `games.script-intent@1`, `games.script-storage-request@1` and `games.script-storage-result@1`. - `games.mod.manage` adds `POST /api/v1/mods/:id/reload` (hot reload of a script mod, storage kept). ## 0.141.0 — 2026-10-11 - `run`: a `site` block with `tld: run` (openvibe.run serves its own front page since Run#28) and `exposure` live with its own public site, so the network's navigation, front door and share cards list it; the old `manifests/products/openvibe.run.json` placeholder entry is removed (the site block is the domain's one home). - `host`: the `site` block describes the hosting catalog (openvibe.host is "hosting compared" since Host#69); OpenVibe's own static hosting is described as built and opening after review. ## 0.140.0 — 2026-10-11 - `actor.agent.manage` and `actor.agent.run` (active), with `actor.agent-manage-request@1`, `actor.agent-manage-result@1` and `actor.agent-run-request@1`: OpenVibe.Actor's personal agents under `/api/v1/me/agents` (standing instructions, a memory the person controls, a schedule; every run an ordinary task, `platform.task@1`). `/api/v1/agents` stays the public catalog of agent systems (`actor.agent.read`). ## 0.139.0 — 2026-10-11 - `host.catalog.manage` (active, staff only) with `host.catalog-manage-request@1` and `host.catalog-manage-result@1`: OpenVibe.Host's hosting catalog (plan T20 step 3), staff entries for providers (affiliate link and its exact disclosure), plans (source URL, currency, monthly price, the date it was confirmed) and promotions. The catalog's reads are public. ## 0.138.0 — 2026-10-11 - Sources category `hosting` (`sources.source@1`, `sources.item@1`, `sources.source-write-request@1` and the `sources.item.created|updated|removed` and `sources.fetch.failed` payloads): official provider APIs whose items are hosting plans, read by OpenVibe.Host's hosting catalog (plan T20 step 3). ## 0.137.0 — 2026-10-11 - `bot.robot-profile@1`: `mapping.driver` may be `looi`, the community's Generic LOOI driver (a Bluetooth bridge plugin for the OpenVibe Link; OpenVibe Link `docs/hardware-looi.md`). OpenVibe.Bot ships the matching `looi` profile. - Service manifests name their PostgreSQL databases (no service declares SQLite any more). ## 0.136.1 — 2026-10-10 - `events.publish-result@1`: `cursor` is required unless the item says `pruned: true`. That answer is a repeat of an event_id whose stored copy retention already removed, so there is no position to return. Events has answered it this way since redaction and retention; `pruned` is now described. Fixtures: a pruned repeat (valid) and an item with neither (invalid). ## 0.136.0 — 2026-10-10 **No per-event sequence number on the consumer wire** (ADR-042 decision 7, its last step; plan T7). Positions became opaque cursors in 0.132.0; the informational `seq` that stayed beside them goes now, with the "global order" promise. Every client dedupes by `event_id` (openvibe-sdk 0.42.0 realtime, Shared 3.0.1), and no receiver reads `seq` (checked across the estate). - `events.publish-result@1`: `{ event_id, cursor, duplicate }` (single and batch items); `seq` is gone and `cursor` is required. - `events.read-result@1`: page items and single reads are `{ cursor, event }`. SSE messages carry `{ event }`. - Unchanged: `gap` ranges (`from_seq`, `to_seq`; informational), and the operator delivery list and replay (`events.delivery-admin-*`), which still page by the hot store's position. - Fixtures follow; ADR-042 records the step. ## 0.135.0 — 2026-10-10 - **Events, AI and Search get `site` blocks** (name, icon, tagline, what, legal profile, position 31-33). All three were already live public products with their own homes (openvibe.events, ai.openvibe.services, search.openvibe.network; `exposure` live/service), but without a site block the network's navigation and front door (Network's frame sites) never listed them. The copy is each product's own description; the icons already exist in openvibe-shared. ## 0.134.0 — 2026-10-10 - `community.resource.read` (owner `community`, first-party, **active**): OpenVibe.Community's resource index, so a person's pastes and spaces appear in OpenVibe.Services' merged index ("Your resources") beside their robots, rooms, streams, watches and Actor tasks. Kinds `community.paste` (id the slug, named by its title, state its visibility, owner its owner; deleted pastes left out) and `community.space` (id the slug, named by its name, state its visibility, owner its creator). Person-owned, so no project and no OVRN. Implemented by `GET /api/v1/resources` and `GET /api/v1/resources/:ovrn` (Community ships the index with its pin to this release). Services reads it once Network grants the `services` principal. ## 0.133.0 — 2026-10-10 **Actor task webhooks** (plan T17). Additive; OpenVibe.Actor implements them with its pin to this release. - **`actor.task-create-request@1`:** gains `webhooks` (1 to 8 items, each an https `url` and the `events` to deliver, which are the names of platform.task@1 `state`). - **Signing:** Actor signs each task's deliveries with a secret it mints for that task. `platform.task@1` gains `webhook_secret` (`whsec_` and 43 characters), present only in the answer that created the task (and an idempotent repeat), never in a read or a list. It is erased once the task has ended and its deliveries are done. - **New `actor.task-webhook@1`** (the delivery body): `{ type: "actor.task.state", delivery_id: "whd_…", state, task }`. - `delivery_id` is stable across retries. - `task` is platform.task@1 as it was then, without the secret. - Signed exactly like an Events delivery (`X-OpenVibe-Timestamp`, `X-OpenVibe-Signature-V2`), so receivers check it with openvibe-sdk/events `verifyDeliveryV2`. - 2xx is delivered; 410 or a URL the egress guard refuses ends it; anything else is retried for about a day. - **Not accepted yet:** a project secret named by reference (`secret_ref`), since project secrets are not built. The request schema leaves it out, so naming one is a 422. - `lib/ids.js` records `webhook_delivery: 'whd'`; the `actor.task.create` capability says what it now does. - **Fixtures:** a request with webhooks; invalid ones for plain http and `secret_ref`; a delivery; invalid deliveries carrying the secret or with a bad delivery id. ## 0.132.0 — 2026-10-10 **An Events position is an opaque cursor and nothing else** (ADR-042 decision 7, plan T7). `events.read-result@1` and `events.read-request@1` drop the numeric positions their own descriptions kept "only until every consumer reads by cursor". Every consumer does now, checked across the estate: Community's relay (Community #56), the SDK's pull, iterate, checkpoints and realtime (openvibe-sdk 0.42.0), Shared's bell and release watcher (3.0.1), Examples (#14) and Chat's e2e test (#64). Services' project archive already read by cursor. - **Pages:** `GET /api/v1/events` answers `{ events: [{ seq, cursor, event }], next_cursor, latest_cursor, gap? }`. `next_after_seq` and `latest_seq` are gone, and `next_cursor`, `latest_cursor` and each item's `cursor` are required (Events always sent them). Without `after=`, a page starts at the oldest retained event; `after_seq` is refused. - **Single event:** `GET /api/v1/events/:event_id` requires `cursor` beside `seq`. - **Checkpoints:** `PUT` takes `{ topic, cursor, carrier? }` with `cursor` an opaque cursor string; a number is refused. `GET`/`PUT` answer `{ consumer, topic, cursor, carrier, updated_at }` with the cursor as stored (null when none is). - **Realtime:** the SSE id is the cursor, and `Last-Event-ID` resumes only from a cursor. - **Per-event `seq`:** stays, documented as informational (not a position). Deleting it, with `X-OpenVibe-Seq` and the global-order promise, is ADR-042's last step; clients already dedupe by `event_id`. - **Fixtures:** pages and checkpoints with cursors only, plus three invalid ones (a numeric page, a numeric checkpoint answer, a numeric checkpoint request). ADR-042 records what decision 7 became. ## 0.131.0 — 2026-10-10 - `openre.resource.read` (owner `openre`, first-party, **active**): OpenRestream's resource index, its stream definitions as kind `openre.stream`, id `std_<ULID>`, named by the title, owner the stream's owner. Archived definitions are left out. Person-owned, so no OVRN. Implemented by `GET /api/v1/resources` and `GET /api/v1/resources/:ovrn` (OpenRestream ships the index with its pin to this release). OpenVibe.Services reads it once Network grants the `services` principal. - `lib/ids.js` records `stream: 'std'`. - Games characters stay out for now: their rows carry no creation time, which a resource summary requires. ## 0.130.0 — 2026-10-10 **Four more authorities serve their resource index.** Status changes only; no schema changed. - `watch.resource.read`, `actor.resource.read`, `bot.resource.read`, `chat.resource.read`: active, implemented by `GET /api/v1/resources` and `GET /api/v1/resources/:ovrn` (Watch #28, Actor #22, Bot #68, Chat #57, all deployed). OpenVibe.Services adds every active `<id>.resource.read` to its authority registry on boot, so a Services on 0.130.0 merges watches, Actor tasks, robots and rooms beside Network's, Media's, Events' and Host's resources. ## 0.129.0 — 2026-10-10 **The resource index's next authorities** (ADR-048, plan T13 step 8 phase 2, the Contracts half). Four planned, first-party capabilities, one for each service whose resources belong in OpenVibe.Services' merged index, each naming the kind its index will carry. Additive and inert: four capability manifests, four service capability lists extended and one id prefix recorded. No schema changed, no route named and nothing activated; Services only reads an authority once its capability is active. - `watch.resource.read` (owner `watch`): watches, kind `watch.watch`, `wch_<ULID>`. One made under a project carries that project and so an OVRN; a person's own watch has neither. - `actor.resource.read` (owner `actor`): tasks, kind `actor.task`, `tsk_<ULID>`, with the project the task was asked under, when there was one. - `bot.resource.read` (owner `bot`): robots, kind `bot.robot`, `rob_<ULID>`, owner the robot's owner. Person-owned (Bot holds no projects), so no OVRN, like Network's user-owned node principals. - `chat.resource.read` (owner `chat`): rooms, kind `chat.room`, id the room's slug, owner its owner. Person-owned, so no OVRN. - **Left out, again:** OpenVibe.Space (its `spaces` table is the forum copy that moved back to Community on 2026-10-08; code hosting and spaces are still to be built). Also Run jobs and Tools jobs: a `job_<ULID>` is what `common.resource-name@1` refuses. OpenRestream streams and Games characters wait for their own survey. - `lib/ids.js` records `robot: 'rob'`. ## 0.128.2 — 2026-10-10 - `events.read-result@1` documents `latest_cursor` (OpenVibe.Events#45), the head of a pull as an opaque cursor, and says to keep `next_cursor` and read on with `after=`. The numeric `after_seq`, `next_after_seq` and `latest_seq` remain only until every consumer reads by cursor (plan T7; Community, Services and the Examples have moved). Additive. ## 0.128.1 — 2026-10-10 - **Descriptions only.** `inventory.index_document.*` name Inventory's real item page, `/items/<id>` on inventory.openvibe.network, not `/definitions/<id>` (that path is the JSON API). The fixtures carry the real URL. - The `run` manifest's exposure note now says what is there: Run places jobs on people's own Bot nodes, and openvibe.run is parked (a 302 to openvibe.network) rather than an OpenVibe.Sites placeholder. ## 0.128.0 — 2026-10-10 - **Record products send their pages to OpenVibe.Search.** `<owner>.index_document.upserted` and `.deleted` (v1) are declared for Work (`job`, `/jobs/<id>`), Rent (`listing`, `/listings/<id>`), Help (`article`, `/a/<domain>/<slug>`, id `<domain>:<slug>`), Inventory (`definition`, `/definitions/<id>`) and Quest (`quest`, `/quests/<id>`), and each service manifest now produces them. The payloads are `search.index-document@1` with the owner, type and id pattern fixed, plus the usual tombstone. Each product sends them through `openvibe-publishing/search-feed` (1.4.0): on the write's transaction, and from a sweep that re-sends only what changed. Search consumes them as `*.index_document.*` once the owners are in `SEARCH_EVENT_OWNERS`. ## 0.127.0 — 2026-10-10 - **Retired addresses leave the registry.** `ai.openvibe.network` and `events.openvibe.network` are no longer domains of the `ai` and `events` services or of their repositories; their last service callers stopped on 2026-10-02 and 2026-09-30, and only crawlers reached them since. The `ai.openvibe.network` "moved" product manifest is deleted, and descriptions now name only `ai.openvibe.services` and `openvibe.events`. The AI repository manifest no longer lists a domain the network does not hold. The AI service manifest no longer describes a SQLite database (AI runs on PostgreSQL, ov_ai). - **`lib/service-auth.js`:** `requireCapability` drops the `legacy` option (the X-Internal-Key compatibility hook, plan T2). Only a Bearer service token opens a route; no consumer passed it any more. ## 0.126.0 — 2026-10-09 - **OpenVibe Live on by default for OpenRestream.** New capability `live.openre.slot.bind` (active, internal): `POST /internal/openre/slots` on OpenVibe.Live with `live.openre-slot-bind-request@1` → `live.openre-slot-bind-result@1`. OpenRestream asks Live for a slot on the person's channel bound to one OpenRestream stream, ingested by OpenRestream, so the stream's sessions show live on openvibe.live/@username through Live's mirror. It is idempotent per stream; 409 `live.no_account` when the person has no Live account yet. Additive. - `manifests/services/openre.json`: OpenRestream's site copy says what it is: an open restreaming platform (one stream to every platform at once, OpenVibe Live on by default, free and open source). ## 0.125.1 — 2026-10-09 - The product at openre.stream is **OpenRestream** (owner, 2026-10-09): an open restreaming platform. Every name people read says so: `manifests/services/openre.json` (`name`, `site.name`, copy), the repository manifest (now `manifests/repositories/OpenRestream.json`, repository `OpenVibers/OpenRestream`, renamed on GitHub the same day), docs and schema descriptions. Ids are unchanged: service `openre`, `openre.*` capabilities and events. ## 0.125.0 — 2026-10-09 - OpenVibe.News, OpenVibe.Reviews, OpenVibe.Trade, OpenVibe.Coupons and OpenRe.Stream are live: each serves its own domain (`exposure.state: live`, `publicSite: service`) after an independent pre-launch security review and its fixes (News#21, Reviews#22, Trade#24, Coupons#22, OpenRe.Stream#25). They replace OpenVibe.Sites placeholder pages (plan T11: Sites is deleted). - admin., api., auth. and themes.openvibe.network describe what they are now: redirects to the openvibe.network page that does their job (Network#107). `manifests/services/sites.json` says what Sites still serves. ## 0.124.0 — 2026-10-09 - ADR-054 §6 amendment, **the Workshop v1**: Workshop kinds (any person may submit a definition, issued by and credited to them, published only after staff review); the first is `network.badge` (a small reviewed image before a person's name, renderer `network.badge.image@1`). Review, free giving within a cap and a daily limit, submission limits and takedown are all specified there. - `inventory/kind@1` gains `workshop`; an `inventory/equipped@1` slot gains `media_id` (the art image, so a renderer needs no second read). - New `inventory/review-request@1` (publish, optionally with a rarity, or reject with a reason, with fixtures). `inventory.definition.review` is active: `POST /api/v1/definitions/:id/review`, staff only. Additive. ## 0.123.0 — 2026-10-09 - ADR-054 amendment, §3 "Grantors": `inventory/definition@1` and `inventory/definition-request@1` gain an optional `grantors` list (services or apps, at most 8). They may grant that one item (only grant: origin earned or granted, idempotent, within the cap, recorded with the grantor as actor), so an item can be earned on one site while another stays its issuer. This is how OpenVibe.Quest gives some of Live's hats as quest rewards. Additive. ## 0.122.1 — 2026-10-09 - `manifests/services/inventory.json`: the site's icon is `inventory`. A site's id is its icon (Network's Frame builds the site list from it), and `games` collided with OpenVibe.Games. ## 0.122.0 — 2026-10-09 **OpenVibe.Inventory goes live (ADR-054, plan T21 step 2).** - `manifests/services/inventory.json`: - status alpha, exposure `live` with `publicSite: service`, at site position 30 (Inventory); - its lifecycle, health, ready and internal origin (127.0.0.1:5030); - it consumes the two account events and owns `inventory.*`. - Six capabilities are active, each naming its routes: `inventory.item.read`, `inventory.item.list`, `inventory.equip.manage`, `inventory.item.grant`, `inventory.item.consume` and `inventory.definition.manage`. `inventory.definition.review` stays planned until creator submissions open. ## 0.121.0 — 2026-10-09 **The OpenVibe inventory, decided (ADR-054, plan T21 step 1).** A new authority, OpenVibe.Inventory, will own items across the network: kinds, definitions, instances, the equipped set and an append-only ledger. - `docs/adr/ADR-054-inventory-authority.md` (accepted): - service `inventory`, `ov_inventory`, loopback 5030, `inventory.openvibe.network`; - issuers grant within their namespace; - nothing is sold, bought, traded or converted until a later money ADR (ADR-012, ADR-025); - Live's cosmetics become the first kinds, migrated convert-verify-delete. - `manifests/services/inventory.json` (placeholder), with seven planned capabilities: `inventory.item.read`, `inventory.item.list`, `inventory.equip.manage`, `inventory.item.grant`, `inventory.item.consume`, `inventory.definition.manage` and `inventory.definition.review`. - Schemas, with fixtures: `inventory.kind@1`, `inventory.definition@1`, `inventory.instance@1`, `inventory.inventory@1`, `inventory.equipped@1`, `inventory.equipped-batch@1`, `inventory.grant-request@1`, `inventory.equip-request@1` and `inventory.definition-request@1`. - Events: `inventory.item.granted`, `inventory.item.consumed`, `inventory.item.revoked`, `inventory.item.equipped`, `inventory.item.unequipped` and `inventory.definition.published`. ## 0.120.0 — 2026-10-09 **Account export and deletion everywhere a person has rows.** Nine more services declare the two account events. - `deals`, `trade`, `tips`, `vip`, `watch`, `services`, `wiki`, `blog` and `bot` consume network.account.export_requested and network.account.deleted (openvibe-sdk/account-data, SDK 0.37.0). Their startup recovery names the redelivery. Codes holds no personal rows since its console moved to Services, so it declares none. ## 0.119.0 — 2026-10-08 **OpenVibe.MediaHub goes live: openvibe.download as a private drive.** Account export and deletion are declared for the six services that answer them. - `manifests/services/media-hub.json`: status alpha, exposure `live` with `publicSite: service`, its lifecycle, health, ready and internal origin (127.0.0.1:4990). It consumes the two account events. openvibe.pics and openvibe.video answer with coming pages, and the video.*, pics.* and download.* capabilities stay planned. - `food`, `help`, `work`, `quest`, `rent` and `actor` consume network.account.export_requested and network.account.deleted (openvibe-sdk/account-data, SDK 0.36.0), and their startup recovery names the redelivery. ## 0.118.0 — 2026-10-08 **The forum returns to OpenVibe.Community** (owner decision 2026-10-08: Space becomes code hosting and "spaces"; the vBulletin/phpBB-style forum lives on openvibe.community again, plan T20). The mirror of 0.110.0, which moved it the other way. - `manifests/services/community.json`: the forum is Community's again — `community.space.read`, `community.space.manage`, `community.thread.read` and `community.post.create` are back in its capability list, `community.thread.created` and `community.post.created` back in its `eventsProduced`, and its notes, its Discord relay drain and its Roadmap-space recovery describe the forum it serves. - Those four capabilities are active again with Community's routes (`GET /s`, `GET /s/:space`, `GET /s/:space/t/:slug`, `GET /s/feed.xml`, `GET /api/v1/spaces/:space/threads`, `POST /api/v1/spaces/:space/threads`, `PUT /api/v1/spaces/:space/members-only`, …). `community.vote.set` names its thread and comment vote routes again, guarded by `community.post.create` and `community.comment.write`; `community.space.manage` is the one active grant without schemas, so `compatibility/capability-schema-gaps.json` lists it where `space.forum.manage` used to be. - The seven `community.*` forum schemas and the two event payloads are active again with their fixtures. The `space.*` ones (`space.forum-space/thread/post`, `space.forum-read-result`, `space.thread-read-result`, `space.post-write-request/result`, `space.thread.created`, `space.post.created`, `space.moderation.action`) are retired and kept loadable, and `compatibility/deprecations.json` names each Community replacement with a window through 1.0.0. The Community records those replacements had are gone. - `manifests/services/space.json`: no forum capability or event, and the site block and notes say Space is code hosting, with "spaces" (apps that use your OpenVibe account and the SDK) coming next, and that its former `/s/*` paths redirect permanently to openvibe.community. Exposure stays live (Space serves a home); its lifecycle drops the forum's work (Discord relay, Roadmap resync, events outbox) and keeps what runs. - `manifests/services/quest.json` counts `community.thread.created` and `community.post.created` again; `manifests/services/network.json` consumes `community.moderation.action` and no longer `space.moderation.action`. - Space ends with no active capability, so `generated/openapi/space.json` is gone. - Generated bundles, types and OpenAPI documents are refreshed for 0.118.0. - `manifests/services/space.json` consumes no events: Space's account export and deletion hooks only ever touched the forum tables (the forum's account data is Community's), so Network stops waiting for Space's part. - `manifests/services/deals.json`: exposure `live` with `publicSite: service` — OpenVibe.Deals serves openvibe.deals (472 DealNews offers with their links kept verbatim and attributed, community votes and watches). - Product copy for the services that launched this week: OpenVibe.Food, Help, Work, Quest, Rent and Watch describe what is live now and what comes next, and each gets the common questions OpenVibe.Help shows. ## 0.117.0 — 2026-10-08 **OpenVibe.Quest and OpenVibe.Rent become services; OpenVibe.Watch goes live with its public site** (plan T18 step 8, T19). - `manifests/services/quest.json` (port 4980, `openvibe.quest` live, site position 27): the shared quest log, counted from the domain event types it consumes through OpenVibe.Events; badges, and OpenCoins through Network when switched on. No capabilities: a quest log is a person's own. - `manifests/services/rent.json` (port 5010, `openvibe.rent` live, position 28): listings people post, safety rules, reports and a staff queue. No capabilities yet. - `manifests/services/watch.json` 0.2.0: exposure `live` with `publicSite: service` (sign-in and watch management on openvibe.watch), its real lifecycle (25 s shutdown, schedule and outbox recovery), health, ready, internal origin and site block (position 29). - The three product manifests point at their services. ## 0.116.0 — 2026-10-08 **OpenVibe.Food, OpenVibe.Help and OpenVibe.Work become services** (plan T19 "flesh the network out"; owner 2026-10-08). - `manifests/services/food.json` (port 4970, `openvibe.food` live, site position 24): food banks and budget grocers near you from OpenStreetMap (ODbL attribution on every answer), the budget food list, meal plans and a pantry. - `manifests/services/help.json` (port 5020, `openvibe.help` live, position 25): the network's help centre built from this repository's catalog, and support tickets. No capabilities: a ticket is a person's own. - `manifests/services/work.json` (port 4960, `openvibe.work` live, position 26): job listings from open job boards with their provenance (excerpt only, original linked), search and saved searches. No capabilities yet. - Capabilities (audience `openvibe.food`): `food.plan.write` (sensitive), `food.plan.read`, `food.pantry.write` (sensitive), `food.pantry.read`; the place search and the food list are public reads with no capability. - Schemas: `food.plan-create-request@1`, `food.plan@1`, `food.plan-result@1`, `food.pantry-request@1`, `food.pantry-result@1`. - The three product manifests point at their services. ## 0.115.0 — 2026-10-08 **OpenVibe.Actor becomes a service** (plan T17, ADR-044; owner direction 2026-10-08: Actor is OpenVibe's own general agent and the router for agent work, "like OpenRouter but for agents"). - `manifests/services/actor.json`: `actor`, port 4950, `openvibe.actor` live with `publicSite: service`; site block "Actor · Your agent for everything" (position 23). The product manifest now points at the service. - Capabilities (audience `openvibe.actor`): `actor.task.create` (POST /api/v1/tasks, cancel; sensitive), `actor.task.read` (a task and its live stream), `actor.task.list`, and the public `actor.agent.read` (the agent catalog with rate cards, and a dry-run route that explains which agent a task would go to). - Schemas: `actor.task-create-request@1`, `actor.task-event@1` (the SSE stream: state, output, end, resumable by seq), `actor.task-list-result@1`, `actor.agent-list-result@1` (kind, trust, what it can do, rate card, availability). - `platform.task@1` is active and Actor implements it. Additive: `project_id` is optional (a person's own task has none), and a new optional `error` { code, detail } says why a task failed. ## 0.114.0 — 2026-10-08 **Developer links point at OpenVibe.Services.** Manifest text only; no schema, capability or event changed. The developer console moved from openvibe.codes to openvibe.services in 0.113.0, so these links follow: - the Host site's "Build on OpenVibe" link; - the "meanwhile" links of openvibe.website, api.openvibe.network, openvibe.events (to the Events API reference) and openvibe.actor; - the "build on it" cards of openvibe.bot and openvibe.actor. openvibe.codes now answers those paths with permanent redirects. ## 0.113.0 — 2026-10-08 **The developer portal moves from OpenVibe.Codes to OpenVibe.Services; Codes becomes the open coding-agent harness** (owner decision 2026-10-08; plan T13 and T16 notes). Codes' portal data in production is empty, so the names move at once. - New in Services' namespace: `services.release.read` and `services.release.manage` (audience `openvibe.services`), the schemas `services.app-manifest@1`, `services.release@1`, `services.release-read-result@1`, `services.release-manage-request@1`, `services.release-manage-result@1`, the event payload `services.moderation.action@1` and the events `services.app.published|deprecated|revoked`. They are the `codes.*` ones under the new owner and audience. - Retired, kept loadable: `codes.release.read`, `codes.release.manage`, `codes.resource.read` and the six `codes.*` schemas (`compatibility/deprecations.json` names each replacement). Network consumes `services.moderation.action`. - `manifests/services/services.json`: live with `publicSite: service`; it owns manifests, releases, trust tiers and playground logs (kinds `services.manifest` `mfs_`, `services.release` `rel_`) besides the merged index; its site block is the developer platform's. `manifests/products/openvibe.services.json` describes the console and docs. - `manifests/services/codes.json`: no capability or event; the site block describes the harness (position 22). ## 0.112.0 — 2026-10-07 **Robots driven through the relay plugin.** `bot.robot-profile@1` is 1.1.0: `mapping.driver` gains `relay`, the OpenVibe Link plugin that forwards `button` and `point` commands to an owner's own script in OpenVibe.Live's hardware-client messages (OpenVibe Link #33; plan T15 R9 steps 4-5). Bot's catalogue profile `relay.generic` uses it for the robots converted from Live's stream controls. Additive: every existing profile stays valid. ## 0.111.0 — 2026-10-07 **Events' public origin is openvibe.events** (plan T7, ADR-042 item 10). Manifest and example changes only; no schema changed. - `manifests/services/events.json`: `publicOrigin` `https://openvibe.events`; `domains` lists `openvibe.events` first and keeps `events.openvibe.network`, which Events still answers until every client has moved (then it answers 308). `manifests/repositories/OpenVibe.Events.json` lists the same two names. - `network.realtime-ticket-result@1`: the `stream_url` description and fixtures use `https://openvibe.events/realtime/stream`. The ticket's audience was already `openvibe.events`. - Product manifests: openvibe.events's launch line names the API and realtime stream there; the realtime notice links to `https://openvibe.events/`. ## 0.110.0 — 2026-10-07 **OpenVibe.Space and OpenVibe.AI's public home launch** (plan T10 step 3, T6). It serves the forum at `https://openvibe.space` from loopback :4940. Community keeps pastes, comments, Pulse and submissions; its former `/s/*` forum paths redirect permanently to Space. - `manifests/services/space.json`: `status` alpha, `exposure` live with `publicSite: service`, health and readiness paths, lifecycle, and the events Space produces and consumes. - `space.forum.read`, `space.forum.manage`, `space.thread.read`, and `space.post.write` are active with Space's implemented routes. `space.post.write` still guards thread creation; `space.thread.write` remains planned as a separate grant. `space.pulse.read` remains planned because Space serves no Pulse. - `space.thread.created`, `space.post.created`, and `space.moderation.action` have payload contracts and valid and invalid fixtures. Space's source is `space`; `space.moderation.action` is `common.moderation-action@1` (ADR-022) and OpenVibe.Network consumes it into the moderation audit log. - Space's problem codes, including `403 space.blocked`, are documented in `docs/space-problem-codes.md`; the problem fixture demonstrates the stable URI and code. - The four former Community forum capabilities are retired and removed from its service list. The seven Community forum schemas remain loadable but retired, with Space replacements and a compatibility window through 1.0.0. Historical `community.thread.created` and `community.post.created` payloads also remain loadable as retired types; Community no longer claims to produce them. Community's planned vote grant now describes its surviving comment route only. - `manifests/services/ai.json`: `exposure` live with `publicSite: service`; `publicOrigin` is `https://ai.openvibe.services` (the home, `/stats` and the developer-app API, AI #23), and `ai.openvibe.network`, still listed in `domains`, answers 301 to it. - Generated bundles, types, and OpenAPI documents are refreshed for 0.110.0. ## 0.109.0 — 2026-10-07 **OpenVibe.Run runs, OpenVibe.Services registers, and Bot learns buttons and points.** Two schemas gained optional shape, Run's job API turned active, and Services registered its first capability and AI its first public one; everything here is additive, so every existing fixture and profile stays valid. - `manifests/services/run.json`: `status` alpha; `exposure` internal (deployed on loopback :4920 on 2026-10-07, Run #1 and #2); openvibe.run keeps its OpenVibe.Sites placeholder until Run has a product surface. - `run.job.submit`, `run.job.read`, `run.job.list`, `run.job.cancel`, `run.job.stream`, `run.job.admin`: active, with the routes Run serves (`POST /api/v1/jobs`, `GET /api/v1/jobs`, `GET /api/v1/jobs/:id`, `POST /api/v1/jobs/:id/cancel`, `POST /api/v1/jobs/:id/stream/ticket` and `GET /api/v1/jobs/:id/stream`, `GET /api/v1/admin/jobs`). - `manifests/services/services.json`: `status` alpha; `exposure` internal (deployed on loopback :4930 on 2026-10-07); openvibe.services keeps its placeholder. `services.resource.read` (owner `services`, first-party, active) is Services' merged resource index over every authority that serves one (ADR-048), readable by a first-party service token holding the capability or by a person's user token scoped to one project they own or belong to. - `common.resource-list-result@1` is 1.1.0: an optional `partial`, the authorities a merged index could not read for this page (`{ service, code }`). A slow or failing authority is omitted, never fails the page (ADR-048). - `bot.command@1` gains the kinds `button` (`name`, and an optional `state` `down|up` for a hold button) and `point` (`x`, `y` in `[0, 1]`). `bot.robot-profile@1` gains `commands.button` (`names`: `label`, optional `key`, `cooldown_ms` and `hold`) and `commands.point` (optional `cooldown_ms`), the widget types `buttons` and `video_click` (the widget type pattern now admits `_`), and `button`/`point` as widget command kinds (plan T15 R9). - `ai.app.run` (owner `ai`, public, active; OpenVibe.AI #22): the AI operations as a developer app (ADR-014, plan T6) — `POST /api/v1/chat`, `/generate`, `/summarize`, `/classify`, `/extract`, `/embed` and `GET /api/v1/runs/:id` for the app's own runs. Every run is metered to the app's project; metered capacity is spent only under the project's tier budget, and a sandbox run (or one no tier budget governs) uses only free and local capacity. - `generated/` gains `openapi/run.json` and `openapi/services.json` (their first active capabilities) and stamps every document with the release version. ## 0.108.0 — 2026-10-07 **OpenVibe.Watch runs, and four authorities serve their resource index.** Status changes only; no schema changed. - `manifests/services/watch.json`: `status` alpha; `exposure` internal (deployed on loopback :4730 on 2026-10-07, Watch #1); openvibe.watch keeps its OpenVibe.Sites placeholder until Watch has a product surface. - `watch.watch.read`, `watch.watch.manage`, `watch.observation.read`, `watch.check.run`: active, with the routes Watch serves (`/api/v1/watches`, `/:id`, pause/resume, `/:id/check`, `/:id/observations`, `/:id/checks`). - `host.resource.read`, `events.resource.read`, `codes.resource.read`, `media.resource.read`: active, implemented by `GET /api/v1/resources` and `GET /api/v1/resources/:ovrn` (Host #28, Events #17, Codes #20, Media #47, all deployed). ## 0.107.0 — 2026-10-07 **OpenVibe.Host is live at openvibe.host** (Stage B launched 2026-10-07; Host#26, Sites#11): `manifests/services/host.json` `exposure` becomes `{ state: live, publicSite: service }`, so Network lists Host as an open site. **The resource index's first authorities** (ADR-048, plan T13 step 8, the Contracts half). OpenVibe.Network has served `GET /api/v1/resources` since 0.104.0; this adds the planned, first-party capability each surveyed authority needs to serve its own — `media.resource.read`, `events.resource.read`, `host.resource.read` and `codes.resource.read` — and names in each the kinds that authority's index will carry. Additive and inert: four new capability manifests, four service capability lists extended, six id prefixes recorded in `lib/ids.js`; no schema changed, no route named, nothing activated. - `media.resource.read` (owner `media`, first-party, **planned**, no routes): Media's index carries its objects, kind `media.object` with a `med_<ULID>` id — the canonical blobs its v1 vods, clips and files are projections of (`media_objects`, migrations/0001_initial.sql). Vods and clips are projections over objects (bigint ids) and are never listed. An object in a developer-project tenant carries that project; an object in a first-party app tenant (`live`, `games`, …) has no project and so no OVRN. - `events.resource.read` (owner `events`, first-party, **planned**, no routes): Events' index carries its subscriptions, kind `events.subscription` with a `sub_<ULID>` id (`server/api/subscriptions.js`), project-scoped when a developer app created them and project-less when a service did. Events themselves (`evt_`) are what `common.resource-name@1` refuses by design, and a project's queue stays out of the index until Events stores a queue row. - `host.resource.read` (owner `host`, first-party, **planned**, no routes): Host's index carries its static sites (kind `host.site`, `sit_<ULID>`), the immutable deploys of those sites (kind `host.deploy`, `dpl_<ULID>`) and their default and custom domains (kind `host.domain`, `dom_<ULID>`), each with its `project_id`. Host renamed its site id prefix from `site_` (four letters) to `sit_` (three) — zero sites exist in production, so nothing converts — so `host.site` now composes an OVRN. Host lists no projects: only Network lists projects. - `codes.resource.read` (owner `codes`, first-party, **planned**, no routes): Codes' index carries its validated app and mod manifests (`codes.manifest`, `mfs_<ULID>`) and its releases (`codes.release`, `rel_<ULID>`), each created in a Network project. Trust tiers are per-app metadata and playground run logs are audit records, neither a resource; Codes hosts no repositories, so the ADR-048 catalog's `codes.repo` is gone. - **Step 8 phase 2 — the person-owned resources** (Bot robots and devices, Chat rooms, Community spaces, OpenRe.Stream streams and Games characters): served like Network's user-owned node principals, no OVRN and owner = user, in a later phase, so no `*.resource.read` capability yet. - **Surveyed and left out** (not even a person-owned resource, so no `*.resource.read` capability): AI (runs and the record groups are keyed to requester principals and attribution subjects, never a project) and Tools (a `job_<ULID>` is exactly what `common.resource-name@1` refuses, and a job expires unless referenced). - **`lib/ids.js` records the chosen prefixes** (`subscription: sub`, `deploy: dpl`, `domain: dom`, `manifest: mfs`, `release: rel`, `site: sit`); `lib/resources.js` still formats any `<service>.<type>` kind generically, so `nameOf` needs no new entry, and `common.resource-name@1` is unchanged. `generated/` gains nothing but the release's version stamp — only active capabilities with routes are emitted, and these are planned with none. ## 0.106.0 — 2026-10-07 **OpenVibe.Bot is a site** (Bot #34). Bot has served openvibe.bot since 2026-10-02 (`exposure` live, `publicSite` service) and, since Bot #34, its own front page; without a `site` block the network navigation and the openvibe.network home did not list it. Additive: one `site` block, no schema changed. - `manifests/services/bot.json` `site`: name `Bot`, icon `bot`, tagline "An open control panel for robots", the front page's one-line description, legal profile `games` (accounts and per-account state: robots, devices and the 30-day command audit), position 21 (after the current entries). The icon id `bot` falls back to the OV mark until openvibe-shared draws a `bot` glyph. `manifests/products/openvibe.bot.json` stays the catalog entry (the block carries no `tld`). ## 0.105.0 — 2026-10-05 **Chat's ticket conversation and DM block-state reads are live** (Chat #26 and #27). `chat.ticket.write` is active and `chat.messages.read` gains Chat's DM block-state route. Additive: one new chat result contract, one capability activated with the routes it names, one route list extended; no existing schema changed. - `chat.ticket.write` (owner `chat`, first-party, **active**): OpenVibe.Chat serves the per-ticket conversation it names (`server/chat/internal-tickets.js`) — `POST /internal/chat/tickets/:ticket_id/messages` creates the (ticket id, calling service) conversation on first use and appends one message, and `GET /internal/chat/tickets/:ticket_id` reads that service's own conversation, paged; both guarded by the capability, so an app, node or agent token is refused. - `chat.dm-block-state-result@1` (new, owner `chat`, first-party): the answer of `GET /internal/chat/dm/block-state?a&b` — `{ blocked }`, true when either user blocked the other, in Chat's `dm_blocks` or as a Network platform block — added to `chat.messages.read`'s route list. It follows the other `chat.*` internal-read schemas: an `anyOf` of the ok shape and `chat.ingress-ack@1`'s error, with valid and invalid fixtures. - `chat.ticket-conversation@1` moves from planned to active in the catalog. `generated/openapi/chat.json` gains both ticket operations (the generator emits an active capability's `implementedBy` routes), and the generated docs move to 0.105.0. ## 0.104.0 — 2026-10-05 **Network's resource index is live** (Network PR #62; ADR-048). `network.resource.read` is active: OpenVibe.Network serves the index it names. No schema changed and no route list changed — the two routes the capability already listed are now implemented and guarded. - `network.resource.read` (owner `network`, first-party, **active**): `GET /api/v1/resources` answers a page of `common.resource-list-result@1` and `GET /api/v1/resources/:ovrn` answers `common.resource-summary@1`, both guarded by the capability. Network's own resources today are its developer projects, apps, keys and nodes. - `generated/openapi/network.json` gains both operations, since the generator emits an active capability's `implementedBy` routes; the OpenAPI version line and the other generated docs move to 0.104.0. - The T2 Fabric offer registry's public routes, which shared `/api/v1/resources`, have moved to `/api/v1/offers` in the same Network release; no Contracts manifest listed those public routes, so no route list changed here. ## 0.103.0 — 2026-10-05 **Chat's three new internal reads and Network's resource index** (Chat PR #24; ADR-048). Additive: three new chat result contracts and one planned capability, two capability route lists extended, no existing schema broken. - `chat.site-daily-result@1` (new, owner `chat`, first-party): the answer of `POST /internal/chat/stats` with `kind: "site-daily"` — `{ days: [{ day, messages, chatters }] }`, one UTC day per entry in `[since, until)`, zero-filled, at most 400 — Live's home series. `chat.stats-request@1` gained `site-daily` in its kind `enum`, with the per-kind `allOf` rule that both `since` and `until` are required, the way its `user` and `stream` kinds already express their required field. - `chat.first-chat-result@1` (`{ first }`, Live's welcome check) and `chat.sound-result@1` (`{ sound }`, reusing `chat.sounds-result@1`'s sound projection) are the answers of the other two reads. All three follow the other `chat.*` internal-read schemas: an `anyOf` of the ok shape and `chat.ingress-ack@1`'s error, with valid and invalid fixtures. - Capabilities: `GET /internal/chat/first-chat` joins `chat.analysis.read`'s routes and `GET /internal/chat/sounds/by-command` joins `chat.sounds.read`'s. - `network.resource.read` (new, owner `network`, first-party, **planned**): OpenVibe.Network's resource index (ADR-048 section 3) — `GET /api/v1/resources` answers `common.resource-list-result@1` and `GET /api/v1/resources/:ovrn` answers `common.resource-summary@1`; listed in `manifests/services/network.json`. The T2 Fabric offer registry's public routes, today sharing `/api/v1/resources`, move to `/api/v1/offers` in Network's own release; no Contracts manifest listed those public routes, so no route list changed here. ## 0.102.0 — 2026-10-05 **The Watch contracts** (plan T18). Additive: eight new planned contracts, four planned capabilities and the Watch placeholder manifest, no existing schema changed. - `watch.watch@1` (new, owner `watch`, first-party, **planned**): a user-defined persistent observation — a source (a push event, a webhook, a conditional HTTP URL, a feed or API, a local Node probe, or a Run check), how a value is extracted, how it is compared with the last observation, the condition that fires, the actions it wakes (notification, event, webhook, Actor task, Codes run, Run job), its budget and retention. Valid and invalid fixtures in `fixtures/watch.watch/`. - `watch.watch-request@1` (the create/patch body), `watch.watch-result@1` (the list/watch and pause/resume/delete answers), `watch.observation@1` (one recorded value, immutable) and `watch.check-run@1` (one check execution, immutable, with the twelve terminal states the service's `check_runs.state` CHECK allows), each with fixtures. The request reuses `watch.watch@1`'s `$defs` rather than restating the source and condition shapes. - Events: `watch.observation.recorded` (subject `watch`, internal, low), `watch.watch.triggered` (internal, important; Network turns it into a `WATCH_TRIGGERED` notification for `payload.recipient`) and `watch.check.failed` (internal, normal, with `consecutive_failures`). **The dossier's `watch.triggered` is named `watch.watch.triggered` here**: the repo's event-type pattern (`test/run.js:123`) requires at least three dot-separated segments, as every existing type has. - Capabilities `watch.watch.read`, `watch.watch.manage`, `watch.observation.read` and `watch.check.run` (all planned, first-party), and `manifests/services/watch.json`, a placeholder like Run's: the OpenVibe.Watch repository does not exist yet, so nothing is built. `manifests/products/openvibe.watch.json` now names `watch` as its service, as `openvibe.run` names `run`. - `contracts/platform/resource-offer.v1.json`: the `capabilities` description now documents `runtime:<class>` for a kind harness beside the reserved `worker:<class>` names (a docs-only change; Codes already advertises them). `lib/ids.js` already carried `watch`/`observation`/`check`, so it is untouched. ## 0.101.0 — 2026-10-05 **The task contract** (plan T17, ADR-044). Additive: one new planned contract and its ADR, no existing schema changed. - `platform.task@1` (new, owner `network`, public, **planned**): one task at OpenVibe.Actor's router — the resource `POST /v1/tasks` creates and `GET /v1/tasks/{id}` reads, modelled on Run's `platform.job@1`. It carries `id` (`tsk_<ULID>`), `project_id`, `requester` (a person or an `agent` subject), the `task` text, `mode` (`cheapest | balanced | best | fastest | private`), `budget` (`per_task_usd`, `per_day_usd`), `state` (`queued | running | verifying | succeeded | failed | cancelled`), `created_at`/`finished_at`, `result`, `cost`, `explanation` (a chain of `platform.placement-result@1`, an escalation appending one), the SSE `progress` descriptor, the `cancel` record and registered `webhooks` (HTTPS only, secret by reference). Valid and invalid fixtures in `fixtures/platform.task/`. - `docs/adr/ADR-044-actor-runtime.md` (new, **Proposed**; plan T17, the build-order item after the engine): the task contract, the adapter interface (capabilities, limits, a reviewed rate card, trust class and BYO keys), modes as `openvibe-sdk/placement` objectives, live signals with hysteresis and immediate failover, a `verifying` state for cheap-first cross-family checks and the task's own tests, and the five ADR-046 trust classes with `private` narrowing to OpenVibe-only. Owner is `network` because no Actor service manifest exists: OpenVibe.Actor is a product page (`manifests/products/openvibe.actor.json`, `noRepo`), and every other `platform.*` contract is Network's too. - `lib/ids.js`: the `task` prefix (`tsk`), so `ids.newId('task')` mints the contract's id. ## 0.100.0 — 2026-10-05 **Three small contract fixes** (plan follow-ups). Additive and widening, no contract removed. - `platform.usage-sample@1` now says what `resource` is: the producing service's `platform.rate-card@1` `metric` for the reading's `provider` (e.g. `tokens`), never a run, job or object id, with a `unit` that is that metric's unit — which is exactly what Billing looks the card up by (`provider` + `resource`, then a unit that fits the metric). Without the description AI shipped `resource` as a run id and no card matched. A valid AI reading, `fixtures/platform.usage-sample/valid/ai-per-metric.json` (service `ai`, provider `openai`, resource/unit `tokens`), and a matching `fixtures/platform.rate-card/valid/ai-tokens.json` ground it. - `platform.placement-result@1`: `selected` is now `type: [string, null]` and says null means no eligible candidate. openvibe-sdk/placement `plan()` already answers `{ selected: null, … }` when nothing qualifies; the string-only schema refused the only shape that is sent, so this is recorded in `compatibility/corrections.json` against v0.99.0. Valid fixture `fixtures/platform.placement-result/valid/none.json`. - `chat.ticket-conversation@1` (new, owner `chat`, first-party, **planned**) and the planned `chat.ticket.write` capability on Chat's manifest: one conversation per (ticket id, calling service), with a posted message whose `author_kind` is `person`, `agent` or `staff`, `author` is the author's subject, `body` the text and `created_at` when it was written. It gives Help's service tokens a path into Chat without the person-only `POST /api/chat/send`; until Chat implements the route the capability stays out of `generated/openapi/chat.json`. Valid and invalid fixtures are in `fixtures/chat.ticket-conversation/`. ## 0.99.0 — 2026-10-04 **The estate, generated** (plan T1 step 3). `scripts/estate.js` reads every checkout under the estate root (default `~/OpenVibers`) — each one's `package.json` pins, `STATUS.json`, `migrations/`, and this repository's `manifests/services/` and `manifests/products/` — and writes one `manifests/repositories/<name>.json` per repository plus `docs/ESTATE.md`, the plan §1.1 table (repository, product, authority, runtime, database, domain, SDK/Contracts pin, deployment, public/private, current track). `node scripts/estate.js --check` exits 1 when either is stale; with no checkout root (CI) it checks `docs/ESTATE.md` against the checked-in manifests and skips the rest. `docs/ESTATE.md` was a hand-written 2026-09-30 table and is now a projection of those records, never edited by hand. Additive, no contract changes. ## 0.98.0 — 2026-10-05 **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.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 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 the machine belongs to, `reason` the caller's text when one was given, and the credential and its hash are never carried. Until now a connected machine learned of a revoke only at its next reauth (≤ 330 s); a consumer can stop it at once. Registered in the catalog, in Network's `eventsProduced`, and on `network.node.manage`'s `events`, with valid and invalid fixtures. Network emitting it inside `server/registry/node-principals.js` is a separate follow-up PR and depends on this one; there is no consumer yet. Additive. ## 0.97.0 — 2026-10-04 **The job contract matches the Link again, and Bot's dispatch answer has a contract** (plan T14 step 6 follow-up). - `platform.job@1`: `artifact` is required for class `code` as well as `function` again — the allOf `if` names both classes and the `artifact` description says so — and the invalid fixture `code-without-artifact` is back. OpenVibe Link already refuses a code job without an exact-version artifact (its `internal/protocol/protocol.go` `JobNoArtifact`), and `run.job-create-request@1` already required it; b88849c had dropped the `code` case from this contract alone. A correction to match the implementations, not a new rule. - `bot.job-dispatch-result@1` (new, owner bot, ADR-034): what OpenVibe.Bot answers OpenVibe.Run's `bot.job.dispatch` calls (Bot `server/api/v1.js`, `server/jobs/index.js`). `POST /api/v1/jobs` (201) and `POST /api/v1/jobs/:id/cancel` answer `{ job, sent }`; `GET /api/v1/jobs/:id` answers `{ job, stdout }`. `$defs.job` is the `run_jobs` row (`state` queued … expired, device, payer project, timing, exit, `usage_read` and the original `platform.job@1` body); `$defs.stdout` is the held stdout (the last 1 MiB, or null). `bot.job.dispatch` names it as its `outputSchema`, so that capability is complete and its row leaves `compatibility/capability-schema-gaps.json`; `generated/openapi/bot.json` now carries the responses. Additive. ## 0.96.0 — 2026-10-04 **OpenVibe.Bot's job dispatch for OpenVibe.Run** (plan T14 step 6, R1c): Run hands a job to a paired Link through Bot. Additive, no breaking changes. One new internal, sensitive capability owned by bot, added to `manifests/services/bot.json`: `bot.job.dispatch` (permission `bot:jobs`, resource constraint `project`, input `platform.job@1`, events `run.job.*`). Service-to-service only (Run's service token); never granted to an app or mod and never delegated to people. It names three routes, so `generated/openapi/bot.json` carries them: - `POST /api/v1/jobs` `{node_id, job, project_id, subject, provider}` dispatches a job (idempotent by job id; `project_id` is required). - `POST /api/v1/jobs/:id/cancel` cancels it. - `GET /api/v1/jobs/:id` returns its state, stdout included. Its `outputSchema` is a recorded gap in `compatibility/capability-schema-gaps.json` until Bot's answer shapes land. ## 0.95.0 — 2026-10-04 **The Services control plane** (ADR-048, plan T13 step 1). Additive: three new catalog ids at 1.0.0, owner `contracts`. `common.resource-name@1` is the OVRN `ovrn:<service>:<project_id>:<type>/<id>`, with the pattern `common.usage-recorded@1` and `common.resource-summary@1` already use. `common.resource-control-request@1` is one control operation OpenVibe.Services sends to the authority that owns a resource (`action`, `resource` or, for `create`, `resource_kind`, `project_id`, `idempotency_key`, optional `params`, `on_behalf_of`, `confirmation_id`, `dry_run`, `trace_id`), and `common.resource-control-result@1` the authority's answer (`state` done, pending, refused or failed, with `result`, `confirmation_required` or an `errors.problem@1`). New `contracts.resources`: `parse`, `format`, `nameOf`, `checkName`, `checkControlRequest` (the resource's project is `project_id`; `create` names a kind, every other action a resource) and `checkControlResult` (`problem` and `confirmation_required` fit `state`). New test `test/resources.test.js`. **OpenVibe.Chat's internal read API** (plan T3, J4b): OpenVibe.Live stops reading its mirrored copy of Chat's tables. Additive. Six new internal capabilities owned by chat (loopback, Chat service token, Live's principal today; never granted to an app or delegated), added to `manifests/services/chat.json`: - `chat.stats.read` (`POST /internal/chat/stats`): `chat.stats-request@1` (`kind` site, user, stream or channel-top, an optional epoch-ms window, `limit` 1-50) → `chat.stats-result@1` (`messages`, `chatters`, `sounds`, `top_chatters`). - `chat.messages.read` (`GET /internal/chat/messages`) → `chat.messages-page@1` (`chat_messages` rows, `max_id`). - `chat.analysis.read` (`GET /internal/chat/timeline`) → `chat.timeline-result@1` (`buckets` of `{t, count}`, `max_id`). - `chat.moderation.queue.read` (`GET /internal/chat/moderation/pending-ip`, `…/relay-users`, `…/relay-users/:id`, `…/tts-override`) → `chat.moderation-queue-result@1`, one shape per route with Chat's `pending_ip_messages`, `hidden_relay_users` and `tts_voice_overrides` rows. `chat.moderation.read` is unchanged. - `chat.sounds.read` (`GET /internal/chat/sounds`) → `chat.sounds-result@1` (`count`, or the `channel_sounds` rows still without a Media asset). - `chat.sounds.write` (`POST /internal/chat/sounds/asset`): `chat.sound-asset-request@1` → `chat.ingress-ack@1`. `chat.send-result@1` 1.2.0: the `POST /internal/chat/messages` answer gains optional `first_chat` (boolean). ## 0.94.2 — 2026-10-04 **`past_due` renewal grace on the billing events** (plan T5 step 11, s1). Additive: no field is required or removed. `billing.entitlement.changed@1` `subscription.status` gains `past_due` (a failed renewal, retried until `grace_until`); new optional `grace_until` (date-time or null) and `renewal_period_end`; `reason` adds `renewal_failed`, `grace_ended` and `credit_refund`. `billing.subscription@1` status lists `past_due`. `billing.transaction.reversed@1` already carried a refund of a credit-paid subscription period (type `refund`, from/to swapped); its description now says so, and a fixture pins it. New test `test/billing-grace.test.js`. Billing and VIP pin this tag. ## 0.94.1 — 2026-10-04 `community.moderation.action@1` catalog version is `1.1.0` (the additive space-moderator actions of 0.94.0). ## 0.94.0 — 2026-10-04 `community.moderation.action@1` accepts the audit events of per-space moderators (OpenVibe.Community PR 15): the `action` values `space.moderator_added` and `space.moderator_removed`, and the target type `space` (`id` the space slug, `owner_subject` the moderator). Additive; fixtures for both actions. ## 0.93.1 — 2026-10-04 **Usage rollups are counts, readings are money** (plan T5 step 8, C1; ADR-031 Counting). Description-only: no schema shape change. `common.usage-recorded@1` says rollups are counts for dashboards and quotas and Billing never consumes a `*.usage.recorded` topic; `platform.usage-sample@1` says `idempotency_key` is the only dedupe key and encodes work identity only, and `service` is the producing service's manifest id; `billing.usage.record` is the only money input to Billing. New test `test/usage-topics.test.js`: only network, codes and zone may list a `*.usage.recorded` topic as consumed. ## 0.93.0 — 2026-10-04 **OpenVibe.Bot's profiles, device protocol, operator commands, pairing and events** (plan T15 R7; ADR-043). The Bot manifest and its capabilities came in 0.85.0; this adds the contracts Bot's code already speaks, taken from Bot `155accc` and OpenVibe Link `1ebf1a7`. Additive: every contract id is new. - `bot.robot-profile@1` (public): a profile as `server/profiles/*.json` ships it and `GET /api/v1/profiles` serves it (null vendor, kind, description, variants and camera; defaulted limits; `halt` always in `commands`). The per-kind `commands` schema is Bot's (`drive`/`ptz` axes, `actuator` number, rgb, tone and bool, `say`, `display`); capability and widget names stay open strings. - `bot.device-message@1`: one frame on the `/device` WebSocket, `v`, `seq`, `ts` on every frame. Server to device: `hello`, `config`, `command`, `estop`, `heartbeat_ack`, `error`, `paired`, `rotate`; device to server: `pair`, `status`, `telemetry`, `ack`, `nack`, `heartbeat`, `reauth`, `estop_state`. Job frames stay `platform.job-frame@1`. The description names both implementations (Bot `server/realtime.js`, Node `internal/protocol/protocol.go`). - `bot.command@1` and `bot.command-result@1`: the `/control` command frame and its `command_result` answer (`ack`, `nack`, `refused` with Bot's refusal code, `expired`, `pending`; `cached` for a repeated id). - `bot.robot-read-request@1`: the query of `bot.robot.read` (`owner`, and `limit`/`before` for the audit page); it is now that capability's input, so its OpenAPI routes list the query parameters. - `bot.pair-request@1` and `bot.pair-result@1`: `POST /api/v1/pair` and `POST /api/v1/devices/bind`. The credential, the publish key and the WHIP URL are shown once; without OpenRe the answer says `video: not_configured`. - Event payloads for the five events Bot produces: `bot.robot.online` and `bot.robot.offline` (`robot_id`, `device_id`), `bot.estop.set` and `bot.estop.cleared` (`robot_id`, `by`, `principal_kind`), `bot.command.refused` (`robot_id`, `kind`, `reason`, `role`; never the value). - `bot.robot.control` lists its `/control` WebSocket bindings (`command`, `estop`, `estop_clear`) as notes, and the events they emit (`bot.estop.cleared`, `bot.command.refused`); the Bot manifest's notes name both WebSockets' contracts. - New `test/bot-manifest.test.js`: the manifest's capabilities and events equal Bot's `STATUS.json` lists. ## 0.92.0 — 2026-10-04 `platform.job@1` gains `inputs` (Media objects pinned by sha256) and the `net` values `none`, `public`, `openvibe-only`. Additive; a v1 worker still refuses an unknown `net` with `nack`. ## 0.91.0 — 2026-10-04 **Chat's service-token ingress** (plan T3 J2, OpenVibe.Chat PR #16). These are the capabilities and request contracts of `/internal/chat/*`, the typed Chat endpoints that replace Live's generic bridge (`POST /internal/live/calls`). All of it is additive. - New active capabilities, owner `chat`, `first-party`, quota class `internal-write`, each with an input and an output schema. They are loopback only and need a service token. They are never granted to an app or delegated to an agent. - `chat.event.publish` (`POST /internal/chat/events`, sensitive): transient frames to a stream, a channel, an owner's streams, global chat, everyone or one person. - `chat.moderation.write` (`POST /internal/chat/moderation`, sensitive): one typed action per request (deletes, IP-approval reviews, relay identities, TTS voice overrides, disconnect, log). It emits `chat.message.deleted` and `chat.moderation.action`. - `chat.cache.invalidate` (`POST /internal/chat/invalidate`): cache hints for a person, a channel, IP approvals and bans. Chat's manifest lists all three. - `chat.message.send` also names `POST /internal/chat/messages`, and `chat.presence.read` names `GET /internal/chat/presence`. Both keep their existing routes. - New contracts under `contracts/chat/`, which follow Chat's validators: - `chat.event-request@1`: target kinds, the 16 frame types, the per-frame fields and the target each frame needs. - `chat.moderation-request@1`: one `oneOf` branch per action, each with its own fields only. - `chat.invalidate-request@1`: at least one hint, and `user_data` needs `user`. - `chat.moderation-result@1`: deleted `ids`, or `changes` and `id`. - `chat.ingress-ack@1`: `{ ok: true }`, or the `{ ok: false, error }` refusal every `/internal/chat` route answers. Every request needs an idempotency `key` of 1–160 characters `[A-Za-z0-9:._-]`. Chat applies it once per caller and route; a reused key with another body gets 409. - `chat.send-request` 1.1.0 adds the `/internal/chat/messages` body (room line or DM, `mirror`, `tts`, `frame`, `first_chat_key`). `chat.send-result` 1.1.0 adds its `{ ok, id, … }` answer and the ingress error. - Fixtures: valid and invalid fixtures for every new contract and for the new branches. ## 0.90.0 — 2026-10-04 **OpenVibe.Run service and job API** (plan T14 R1, ADR-034). The contract OpenVibe.Run is built against; everything is planned (Run's repository does not exist yet) and additive. - New service manifest `manifests/services/run.json`: `run`, status and exposure `placeholder`, domain `openvibe.run`, lifecycle all `none` (not built). `manifests/products/openvibe.run.json` gains `relationships.service: run` and keeps `noRepo: true`. - New planned capabilities, owner `run`, each with an input and an output schema: `run.job.submit` (sensitive: it bills the project), `run.job.read`, `run.job.list`, `run.job.cancel`, `run.job.stream` (public, project-scoped) and `run.job.admin` (internal operator access to every project's jobs). `run.admin` is spelled `run.job.admin` because a capability id has three segments. - New contracts under `contracts/run/`: `run.job-create-request@1` (class from `platform.runtime-class@1`, artifact required for `function` and `code`, `inputs` as Media objects pinned by sha256, `limits` wall/cpu/mem as `platform.job@1`, `ttl_ms`, `egress` `none | public | openvibe-only` as Node's `worker.egress`, `requirements` as `platform.workload-requirements@1`, `idempotency_key`), `run.job-create-result@1`, `run.job-read-result@1` (states `queued | placed | running | succeeded | failed | cancelled | expired`, placement node, timings, exit, `usage.seconds`, result, error, with per-state rules), `run.job-list-query@1`, `run.job-list-result@1` (`next_cursor` pages), `run.job-stream-ticket-result@1` (one-use, two-minute ticket, as Events' realtime ticket) and `run.job-stream-event@1` (SSE `output | state | end`). - New planned events produced by Run: `run.job.queued`, `run.job.started`, `run.job.succeeded`, `run.job.failed`, `run.job.cancelled` and `run.job.expired`, projections of the job without args, inputs, result or output. - `ids.newId('job')` mints `job_<ULID>`, the `platform.job@1` id Run passes to the node, so the metering key stays `run:<job id>:<n>`. - Fixtures: valid and invalid for every new contract; `test/platform-run.test.js` pins the Run job id, artifact, limits and egress to `platform.job@1` and Node, and `usage.seconds` to the sum of the job's readings. ## 0.89.0 — 2026-10-03 **Agent tokens and confirmations** (plan T2 step 9; OpenVibe.Network `docs/t2-projects-and-grants.md` §2 gaps 1–3, §4 and §5). These are the contracts that Network WS-Z2 slices 7–9 wait on. All of it is additive. - `identity.service-token-claims` 1.3.0: `sub` also accepts `agent:agt_<ULID>` (`ids.principalSub({ type: 'agent' })`) and `actor_type` gains `agent`. An agent token must carry `project_id`, `env` and `on_behalf_of` (the owner, `usr_`). It may carry `cap_confirm` (capabilities held in confirm mode: each use needs an approved `network.confirmation-request@1`, and they never appear in `cap`, so a receiver that does not know agents refuses them) and `act` (RFC 8693 actor claim: `{ sub: 'svc:<slug>' | 'app:app_<ULID>' }`, the host, nothing else). `if`/`then` rules tie an `agent:` sub to `actor_type: agent` in both directions. Service, app, mod and node tokens validate as before. Fixtures: valid `agent-service-host` and `agent-app-host-auto-only`; invalid `agent-without-owner`, `agent-sub-as-service`, `agent-actor-with-service-sub` and `agent-bad-actor-claim`. - New capability `network.confirmation.manage` (planned, first-party, listed by `manifests/services/network.json`). It is the owning service's side of a confirmation: create one (`POST /internal/confirmations`), read it, consume it once with the action's digest, or cancel it. The owner's inbox (`/api/v1/confirmations`: list, approve, deny) runs on the owner's own session, and no token holds it. The request and response bodies are Network-local until the routes are built. - New event `network.confirmation.changed@1` (planned, produced by Network), the shape of Network's proposal (`docs/contracts-proposal/network.confirmation.changed.v1.json`). Payload: `confirmation_id` (`cnf_`), `agent_id` (`agt_`), `project_id`, `capability`, `audience` (the owning service), `state` (`pending | approved | denied | expired | cancelled`, the names of `network.confirmation-request@1`) and `change` (`created | approved | denied | expired | cancelled | used`), and optional `standing_rule`, `rule_id`, `cancel_reason`, `expires_at` and `changed_at`. `created` leaves the state pending, or approved when a standing rule covers it; `used` leaves it approved; every other change names its state. `cancel_reason` is required on cancelled and absent otherwise. The event never carries `summary` or `details`. Fixtures: valid `requested`, `approved-by-owner`, `approved-by-standing-rule`, `used`, `cancelled` and `expired`; invalid `requested-state`, `cancelled-without-reason`, `used-not-approved`, `missing-audience` and `with-summary`. ## 0.88.0 — 2026-10-03 **The Fabric ADR and the user-owned trust rule** (plan T1 step 3; T14 S2, the Contracts half). ADR-046 "The universal adaptive fabric" (Proposed, for the owner) ties `platform.resource-offer@1`, `workload-requirements@1`, `placement-result@1`, `placement-plan@1`, `provider-state@1`, `rate-card@1`, `cost-snapshot@1`, `telemetry-sample@1` and `usage-sample@1` to one placer (`openvibe-sdk/placement`), names the five trust classes, the `worker:<class>` capabilities a Link advertises (`worker:function`, `worker:code`, …) and the plane rule: the data plane keeps running on the last valid signed plan (`signature`, `epoch`, `expires_at`, checked by `placement.verifyPlan`). It records that `user-owned` is **not** in the default trust set: a user-owned offer is eligible only for a workload whose requirements name `user-owned`, so the SDK's default trust list stays the other four (this replaces the 0.87.0 note that the SDK should route to user-owned nodes by default). Whether a user-owned node may run other users' work, and its consent and revocation rule, are open questions for the owner. - `platform.resource-offer@1` `trust` and `platform.workload-requirements@1` `trust` gain descriptions stating that rule; the enums (1.1.0 since 0.87.0) are unchanged. Consumers are unchanged. - Fixtures: `user-owned-node` now also advertises `worker:function`; new invalid `unknown-trust` (an offer with `trust: "stranger"`, refused on `/trust` alone). `owned-node` is unchanged. **Two product manifests** (plan T11 step 1): `manifests/products/openvibe.zone.json` (served by the planned `zone` service) and `manifests/products/openvibe.work.json` (no repository yet), with name, tagline, pillars and launch taken from OpenVibe.Sites `sites.json`. The catalog test's Sites snapshot holds 35 domains. - `network.project.read` is active, implemented by OpenVibe.Network `GET /internal/projects/:project_id`. **Watch ids** (plan T18, ready now): - `lib/ids.js`: `PREFIX.watch = 'wch'`, `PREFIX.observation = 'wco'` and `PREFIX.check = 'ckr'`, so `ids.newId('watch')` gives the `wch_<ULID>` id the `common.resource-summary` fixture `watch` already carries. None is a subject type or a principal. **Harness capability vocabulary** (plan T17, ready now): - `platform.harness-offer@1` `capabilities` gains the optional booleans `edit`, `review`, `tools`, `vision`, `browser` and `computer_use`, and an optional `runtimes` array (unique; `function`, `code`, `browser`, `linux`, `desktop`, `gpu`, the `platform.runtime-class@1` classes). `capabilities` keeps `additionalProperties: false`, and the entry stays 1.0.0, as it did when 0.86.0 added optional fields. New fixtures `capability-vocabulary` (valid, every new key) and `unknown-capability` (invalid, a `gpu` key: a runtime class belongs in `runtimes`). Additive. **Resource names in the resource index** (plan T13, ready now): - `common.resource-summary@1` is 1.1.0: an optional `ovrn` (ADR-034 section 2) with the pattern of `common.usage-recorded@1` `resource`, naming the summary's own service, project and id. New fixtures `with-ovrn` (valid) and `ovrn-without-project` (invalid). Additive. ## 0.87.0 — 2026-10-03 **Host git sources** (plan T12 Stage B): a Host site may name a public Git source — provider, repository URL and branch; never a credential — and a site with a source also accepts a CI-built deploy at `/sites/:id/source/deploys` naming the ref and full `commit_sha`. That deploy is always a preview and records the commit as immutable provenance. - `host.site.manage` gains `GET`, `PUT` and `DELETE /api/v1/sites/:id/source`; `host.deploy.create` gains `POST /api/v1/sites/:id/source/deploys`. - `host.deploy@1`: the `source` enum adds `preview` and `git`, and an optional `git` object carries `provider`, `repo_url`, `ref` and a 40-hex `commit_sha`. New valid fixture `git`. Additive: every existing deploy stays valid, and the object is present only on `source: git`. **The Bot service manifest** (plan T15 step 1; ADR-043; supersedes #8). OpenVibe.Bot has run on openvibe-ovh since 2026-10-02 (`openvibe-bot.service`, `/opt/openvibe.bot`), and `ovhost validate` found no `bot` manifest to read its lifecycle from. - `manifests/services/bot.json`: status `live` on `openvibe.bot`, internal origin `127.0.0.1:4630`, the five `bot.*` events it produces, and all six lifecycle parts. Shutdown is what Bot's `openvibe-sdk/service` `gracefulStop` does: stop the job timers and Network key refresh, close the device and operator WebSockets and the outbox relay, drain HTTP for up to 4 s, close PostgreSQL and Valkey, exit 0, with a 5 s hard deadline inside the unit's 10 s `TimeoutStopSec`. The contracts range `>=0.85.0 <1.0.0` covers the v0.85.0 Bot installs. - Three active capabilities, each bound to the routes whose guard checks it in the deployed Bot (dae7c59), for a Network service token: `bot.robot.read` (list by `?owner=`, a robot, its operators and devices), `bot.robot.manage` (create, update, delete, pairing codes, operators, the command audit, clearing the e-stop, rotating and revoking a device) and `bot.robot.control` (latching the e-stop, which also needs `bot.robot.read`, and the operator WebSocket, which acts for `X-OV-Subject` with that person's role). Their resource constraint is `none`: Bot does not check the acting person against the robot for a service token on the REST routes, and the descriptions say so. All are first-party, none is a staff capability, and each names an input and an output schema: new `bot.robot@1`, `bot.device@1`, `bot.robot-read-result@1`, `bot.robot-manage-request@1`, `bot.robot-manage-result@1` (which includes the audit page and `bot.device-connect-result@1`), `bot.robot-control-result@1` and `bot.device-connect-result@1`, with valid and invalid fixtures. - `bot.device.connect` is `planned` (the Bot manifest lists it, as owners list their planned capabilities): Bot declares the name but no route checks it, so it names no routes or schemas until one does. - `openvibe.bot` product: names its service `bot` and repository `OpenVibe.Bot` instead of `noRepo`. - Presence policy: `bot.robot.online` and `bot.robot.offline` are robot connectivity (ADR-043), not a person's presence, and are allowed. Any other `.online`, `.offline` or presence event type is still refused. **Media placement events** (follow-up of OpenVibe.Media PR #18, "Record placement decisions for replicas and provider transitions"; ADR-026). Media records every replica move and provider transition in its placement outbox, but the two provider events used types starting with `provider.`, which OpenVibe.Events can never accept from source `media`: its prefix gate (`server/config.js` `sourcePrefixes`, `server/api/publish.js`) requires an `event_type` to start with the source key, so both were permanently rejected (`403 events.type_not_allowed`) and piled up in `event_outbox`. The types now live in the media namespace (`media.provider.health_degraded`, `media.provider.capacity_warning`), and all six event types Media emits today gain active payload contracts owned by `media`, listed in its manifest: - `media.replica.requested`, `media.replica.ready`, `media.replica.draining`, `media.replica.evicted` (Media's `server/objects/tiering.js`: the promote/demote decisions, recorded inside the transaction that starts the copy, upserts the verified location, starts the delete and removes it). - `media.provider.health_degraded` and `media.provider.capacity_warning` (Media's `server/placement/providers.js`: the healthy→unhealthy flip and the class loss after a completed probe), each emitted once per transition, not per tick. Each payload is deliberately narrow — the object id and tenant, placement class, action, providers, key/size for a replica, the class set before and after a loss — never the object's bytes, title or metadata. `media.replica.*` was already inside the streamable namespace; only the provider types changed. Valid and invalid fixtures for each, and `test/run.js` asserts every type Media produces starts with `media.`, is owned by `media` and is listed in its manifest. **User-owned trust class and the estate table** (plan T1 step 3; ADR-034 §5 control plane/data plane; the Fabric ADR, ADR-046, is still to write). A person's own node or machine is now a trust class of its own in Contracts, between `first-party` and `partner`. The SDK placement planner's runtime already honours an explicit `trust: ["user-owned"]` requirement, but its default trust list and its TypeScript `Offer` type omit the value, so the SDK needs code and type changes as well as a pin to this release to route to user-owned nodes by default. - `platform.resource-offer@1` 1.1.0 and `platform.workload-requirements@1` 1.1.0 add `user-owned` to their `trust` enum, ordered `first-party`, `user-owned`, `partner`, `community`, `external`. Additive: a producer or consumer that knows only the previous four values is unchanged, and no existing offer or requirement is invalidated. New valid fixtures `user-owned-node` (an offer with `trust: "user-owned"`) and `user-owned-only` (requirements whose `trust` is `["user-owned"]`); the existing first-party `owned-node` fixture stays. - `platform.usage-sample@1`: `route_epoch` and `trace_id` gain descriptions; their types and shape are unchanged. - `docs/ESTATE.md`: the plan §1.1 estate table as a data-only document (repository, product, authority, runtime, database, domain, SDK/Contracts pin, deployment, public/private, current track). Step 2 replaces it with the table generated in CI. **Named object zones for OpenVibe.Zone** (ADR-031 amendment 2026-10-02; plan D37). The amendment maps named Zone buckets onto Media's S3 surface. A zone is a Media namespace, and Media keeps the one object catalog and the only deletion path. - **Ownership:** Zone owns the zone record and Media owns objects, bytes and replicas. - **Names:** zone `zon_<ULID>`, `ovrn:zone:<project_id>:object-zone/<zone_id>` and Media namespace `<environment root>.<zone_id>`. - **Buckets:** the bucket is the zone name, resolved within the signing key's project and environment. The environment root is the zone `default`, so no existing object moves. - **Grants and quotas:** grants are scoped to a zone's resource name. A per-zone byte quota sits under the environment quota. - **Usage:** Media emits per-zone usage rollups that name the zone's resource name in a new optional `resource` field of `common.usage-recorded@1` 1.1.0 (additive; existing producers are unchanged) and its id as `dimension`. `contracts.usage.checkUsageRecorded` refuses a rollup whose `resource` names another project than `project_id`, and `contracts.zones.checkObjectZoneUsage` a usage answer whose `ovrn` names another zone than `zone_id`. - **Deletion:** one lifecycle for every door, the one `media.object.delete` already promises: a held object's deletion is refused, a tombstone is restorable until the retention period ends, and the purge then erases every replica, B2 file versions included, within 24 hours. Zone deletion tombstones with no restore window and answers `409 zone.held` while an object is held. - **No name reuse:** a `deleting` or `deleted` zone's name is retired in its environment (`409 zone.name_retired`; `provisioning` and `active` answer `409 zone.name_taken`), so a signature, which covers the key and the bucket name, always names one zone id, whatever signing time it carries. `contracts.zones.checkObjectZoneList` refuses a list in which two zones of one environment share a name. - **List query:** `limit` is 1 to 200 as an integer or as a query-string value. - **Migration:** Media backfills a key derived from each object's namespace and `med_` id, legacy child namespaces included, before S3 accepts writes. The default zone's access is `per-object`, so existing public and unlisted objects keep their URLs and cached copies. - **Control API:** a planned Zone service manifest (`manifests/services/zone.json`) with six planned capabilities, one per route. The capabilities enter `generated/openapi` when they turn active. - **Contracts:** new `zone.object-zone@1` and its create, update, list, list-query, delete-query and usage contracts, all `planned`, with fixtures. `contracts.zones.checkObjectZone` also checks that a zone's OVRN and Media namespace name its own project and id. Additive; no active contract changes. **Reserved worker capability names for OpenVibe Link** (plan T14 groundwork, additive): a Link advertises its runtimes as `worker:` capabilities in `platform.resource-offer@1`. The names are reserved in the contract now, not only by the `capabilities` pattern: `platform.resource-offer@1` gains `$defs.reservedWorkerCapabilities`, the six names `worker:function`, `worker:code`, `worker:browser`, `worker:linux`, `worker:desktop` and `worker:gpu` — one per `platform.runtime-class@1` class (`worker:` + the class) — and both the `capabilities` description and `platform.runtime-class@1` say so. A node advertises the name of each class it runs, so `worker:function` today and `worker:code` when the `code` class ships. Additive to validation: every existing offer stays valid, and other `worker:` names (`worker:ffmpeg`, `worker:ai-gpu`) stay ordinary capability names. New valid fixture `worker-classes` (an offer advertising all six reserved names). Four new valid fixtures for `platform.runtime-class@1` (`code`, `browser`, `linux`, `desktop`) complete its six `const` classes. ## 0.86.0 — 2026-10-03 **Harness offers for Fabric routing** (plan T16 step S5): OpenVibe.Codes publishes its harness catalog as `platform.resource-offer@1` offers of kind `harness` whose `detail` is a `platform.harness-offer@1`; the envelope keeps `trust`, `health`, `latency_ms`, `pricing` and `capabilities`. - `platform.harness-offer@1` gains four optional fields: `task_capabilities` (unique; `edit`, `review`, `browse`, `run`, `test`, `plan`), `runtime_needs` (unique runtime names such as `git`, `node`, `python`, `docker`; pattern `^[a-z][a-z0-9.-]*$`, at most 40 characters), `byo_key` (the caller supplies their own provider key) and `success_rate` (0 to 1, the measured share of routed tasks that finished green). The example carries them; fixtures `fabric-routing` (valid), `unknown-task-capability` and `success-rate-over-1` (invalid). - `platform.resource-offer@1`: no schema change. The `capabilities` description names the harness convention: `task:edit`, `task:review`, `task:browse`, `task:run`, `task:test`, `task:plan`, `harness:mcp`, `harness:resume`, `harness:host-access`, `harness:long-autonomy`. New valid fixture `harness-task-routing`. Additive. **Run jobs and their metering** (plan T14 lane G, follow-up 1 of the Link gap audit): - New `platform.runtime-class@1`: the classes `function`, `code`, `browser`, `linux`, `desktop` and `gpu`, names only; `function` is the first one implemented. - New `platform.job@1`: `id` (`job_<ULID>`, the idempotency and ack key), `class`, `artifact` {`name`, `version`} (required for `function`), `args`, `ttl_ms`, `limits` {`wall_ms`, `cpu_ms`, `mem_bytes`} and `net` (only `deny`, the default). - New `platform.job-frame@1`: the job frames on the device control link, in its `v`/`seq`/`ts` envelope. Server to device: `job`, `job_cancel`, `job_exit_ack`. Device to server: `job_started`, `job_stdout` (`chunk_seq`, because `seq` is the envelope's), `job_usage`, `job_exit` (`reason`, `code`, `result`, `usage`). - Metering, documented in `job_usage` and `job_exit`: one `platform.usage-sample@1` reading per wall-clock second, `run:<job id>:<second>`, service `run`, operation `function.invoke`, unit `s`, quantity 1, or the fraction for the partial last second, which only `job_exit` reports. Every field comes from the job and the second, so resends and the `job_exit` backfill replay in Billing instead of being billed twice. The link's terminator (Bot now) writes the readings. `usage-sample` gains a description sentence, a `function` example and two fixtures; no field changed. - `test/platform-run.test.js` checks the derivation. Additive. ## 0.85.0 — 2026-10-02 **Billing records usage readings** (plan T5 lane F): the capability `billing.usage.record` (active, internal, `billing:usage`, resource constraint `none`, quota class `ledger-write`), released from OpenVibe.Billing's proposal and listed by `manifests/services/billing.json`. `POST /api/v1/usage` takes one `platform.usage-sample@1` reading (its `inputSchema`) and stores it without moving money; the reading's `idempotency_key` deduplicates retries (same reading: 200 with the stored row; a different one under the same key: 409 `billing.usage_key_reused`). Listing all readings (`GET /api/v1/usage`) stays under `billing.ledger.admin`. New `billing.usage-record-result@1` (its `outputSchema`): `{ record }` with the stored row's `id`, `idempotency_key`, `project`, `subject`, `service`, `at`, `received_at`, `principal` and the `reading` itself; fixtures `recorded` (valid) and `reading-without-idempotency-key` (invalid). 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 OpenVibe.Network `docs/t2-cells-and-node-principal.md` §9.1): - `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. - `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 paired; `nodes:pair`, `nodes:read`, `nodes:revoke`), `network.node.self.manage` (a paired machine presents its own capabilities and rotates its own credential; `nodes:self`) and `network.resource.report` (a source reports its complete set of `platform.resource-offer@1`; `resources:write`), all active, internal and listed by `manifests/services/network.json`. Their bodies are Network-local, so they name no schemas and are recorded in `compatibility/capability-schema-gaps.json`. `network.node.report` is also implemented by `POST /internal/registry/instances/report`. - `platform.resource-offer`: `kind` gains `storage`, `delivery`, `runtime`, `agent` and `harness`; the new optional `detail` is required for those kinds and validated by the kind's own `platform.<kind>-offer@1`, and is forbidden for `node` and `provider`. Fixtures `storage`, `delivery`, `runtime`, `agent`, `harness` (valid, beside `owned-node` and `provider`), `storage-without-detail` and `node-with-detail` (invalid). - Optional fields: `platform.service-instance` `route_weight` (integer 0-1000); `platform.node-capabilities` `capabilities` (resource-offer capability form), `agent_version` (at most 40 characters) and `updated_at` (date-time); `network.node` `cell` (`^[a-z]{2,8}-[0-9]{1,3}$`). Fixtures `route-weight`, `agent-report` and `with-cell` (valid), `route-weight-over-1000`, `bad-capability` and `bad-cell` (invalid). Additive. ## 0.84.0 — 2026-10-02 Add nine public `platform.*@1` schemas: usage and telemetry samples; node capabilities and service instances; runtime, storage, delivery, agent, and harness offers. Each has valid and invalid fixtures. ADR-034 is proposed. Generated types and bundle include all nine. `platform.usage-sample@1` can carry all 15 plan T5 usage fields in one reading (lane F). It gains four optional fields: `free_allowance_used` (non-negative number in the reading's own `unit`, at most `quantity`), `vibes_charged` (non-negative integer count of vibes-bits, Billing's ledger minor unit; absent means not rated), and `route_epoch` and `trace_id` (same types as in `platform.telemetry-sample@1`). Readings without these fields still validate. New fixtures: `all-fifteen-fields` and `minimal` (valid); a fractional or negative `vibes_charged` and a negative `free_allowance_used` (invalid). Additive. The OpenVibe.Sites catalog (`sites.json`, 33 domains) moves into Contracts so Sites can be deleted. Additive. - `registry.service-manifest` 1.1.0: `site` gains the optional `tld`, `accent`, `description`, `pillars`, `faq`, `keywords`, `vision`, `highlight`, `launch` and `relationships` (`service`, `plannedRepo`, `noRepo`, `linksLead`, `links`, `meanwhile`, `statusApi`), defined once in `registry.product@1`'s `$defs`. Filled from the catalog for the ten services whose site presents their catalog domain: news, reviews, tips, vip, trade, host, deals, coupons, openre (openre.stream) and space. - `registry.product@1` (new) and `manifests/products/<domain>.json` for the other 23 catalog domains, with no invented service manifests: the Network sub-domains (auth, api, admin, themes), the media-hub brands (video, pics, download), ai.openvibe.network (moved) and ai.openvibe.services, realtime (closed), status, openvibe.events and the planned products (food, quest, rent, homes, services, run, website, help, bot, watch, actor). `relationships.service` names the manifest that serves a domain when one does. - `contracts.products` (`manifests`, `get`, `siteDomain`, `catalog()`); `test/product-catalog.test.js` asserts each of the 33 snapshotted Sites domains has exactly one home and every home validates. ## 0.83.0 — 2026-09-29 **Host site configuration** (plan T12 J3): the capability `host.site.config` (active, first-party, `host:write`, project role checked by Host) with `host.site-config@1`, `host.site-config-request@1` and `host.site-config-result@1` and their fixtures, listed by `manifests/services/host.json`. GET, PUT and DELETE `/api/v1/sites/:id/config` read, replace and reset a site's response headers (the platform's security, routing, caching, scope and transport headers and every X-Forwarded-* are refused), its local-only redirects and its SPA fallback. Additive. ## 0.82.0 — 2026-09-29 **Space and the media hub: the forum moves off Community and the three new products register** (plan T10, D1–D4). - Two service manifests, both `status: placeholder` / `exposure.state: placeholder` (the schema has no `planned`; a placeholder is "charter only", which is what these are until each serves): `manifests/services/space.json` (OpenVibe.Space, `openvibe.space`) and `manifests/services/media-hub.json` (OpenVibe.MediaHub). The media hub carries all three brand domains in one manifest (`domains: [openvibe.video, openvibe.pics, openvibe.download]`, the same list mechanism Network/Live/OpenRe/Games already use); the per-brand nav entry awaits the ADR-039 brand mechanism (T11). - The forum capabilities are renamed off Community: `community.space.read` → `space.forum.read` (active), `community.space.manage` → `space.forum.manage` (planned), `community.thread.read` → `space.thread.read` (active), `community.post.create` → `space.post.write` (active), plus the new planned `space.thread.write` and `space.pulse.read`. The four old capability manifests stay loadable with `status: deprecated` and are still listed by `community` until their window closes. - The seven forum contracts are re-owned under `space.*` (`space.forum-space/thread/post`, `space.forum-read-result`, `space.thread-read-result`, `space.post-write-request/result`) with fixtures; the `community.*` originals go `deprecated` in the catalog and get a `compatibility/deprecations.json` record naming the replacement and window (compat vs v0.80.0: no breaking changes). Contracts only exist for the three active capabilities. - The smallest honest set for the media hub, all planned: `video.vod.read`, `video.playlist.read`, `video.playlist.manage`; `pics.image.read`, `pics.image.upload`, `pics.album.read`, `pics.album.manage`; `download.file.read`, `download.file.upload`, `download.share.create`. No media-hub capability is active, so none has a request/result contract yet. ## 0.81.0 — 2026-09-29 **Opaque cursors beside the global seq** (plan T7, ADR-042, additive). - `events.publish-result@1` gains an optional `cursor` per stored event; `events.read-result@1` gains `cursor` per event, `next_cursor` per page and `cursor` on a single event. A cursor is opaque (a position plus a retention epoch): hand it back as `after=` or `Last-Event-ID`, never parse it. Events returns both `seq` and `cursor` for one release, then `seq`, `next_after_seq`, `latest_seq` and the global-order promise go. - ADR-042 (the events fabric) is recorded in docs/adr. ## 0.80.0 — 2026-09-29 **`chat.moderation.read`: how Live reads the chat tables Chat owns** (plan T3, the six staged tables). - New internal capability (owner `chat`, active): `GET /internal/moderation/channels/:channelId` (a channel's moderation settings, every `channel_moderation_settings` column with Live's defaults when there is no row, and its moderator ids), `GET /internal/moderation/users/:userId/channels` (the channels a person moderates) and `GET /internal/moderation/channels/:channelId/emote-count`. Loopback only, service token. - Result contracts with fixtures: `chat.channel-moderation-result@1`, `chat.moderated-channels-result@1`, `chat.emote-count-result@1`. Chat's manifest lists the capability. ## 0.79.0 — 2026-09-29 **Service-token capabilities for every internal call that still used X-Internal-Key** (plan T2, register C-50–C-58). - New internal capabilities, each with request/result contracts and fixtures: `network.avatar.write` (a site reports an avatar change to Network), `network.registry.read` (the resolved URL registry), `network.coins.read` (OpenCoins totals, never balances of named people), `live.avatar.write` (Network pushes an avatar to Live), `live.url_registry.refresh`, `live.analytics.read` (aggregate totals for Network's navigation), `media.avatar.ingest` (Network asks Media to import an avatar), `tools.analytics.read` (the Tools gateway's and tool sites' aggregate analytics). - Avatar URLs in the avatar requests must be on `https://openvibe.media/`. - The owning services' manifests list them. ## 0.78.0 — 2026-09-29 **OpenVibe.Realtime is gone from the estate** (plan T0). - **The `realtime` service manifest is removed** (`manifests/services/realtime.json`). ADR-005 (accepted 2026-09-22) closed OpenVibe.Realtime: browser realtime is a delivery plane inside OpenVibe.Events, and the service was never built, so the estate described something that does not exist. The ADR-005 contracts it did define are unaffected and stay: `identity.realtime-ticket-claims@1`, `network.realtime-ticket-result@1` and `network.notification.created@1` are all first-party Network contracts. No capability, event, namespace or contract referenced the service, so nothing else changes. Reopening it, as ADR-005 says (measured connection counts or fan-out latency that need an independently scaled process), would be a new ADR and a new manifest. ## 0.77.0 — 2026-09-28 **Agents, confirmations, the adaptive fabric and the resource index** (roadmap section 4C: WS-Z1, WS-Z2, WS-Z3, WS-Z7, WS-Z9). - `identity.subject-ref@1` gains the **agent** subject (`agt_<ULID>`): an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated. `ids` knows the `agt` and `cnf` prefixes; `principalSub` accepts agents (`agent:agt_…`). - `events.event-envelope@1`: optional `on_behalf_of`, the person or project an agent or app acted for. - `capabilities.capability@1`: optional `sensitive`. Sixteen capabilities with external side effects are marked (money movement, publishing, sending messages, deleting): an agent needs its owner's confirmation for them unless a standing rule covers it. - `network.confirmation-request@1`: a sensitive action waiting for its owner (pending, approved, denied, expired, cancelled; once, session, until, always). - `events.delivery-policy@1`: an event type's delivery class (realtime_ephemeral … webhook), durability, per-key ordering, latency targets, retention tiers, routing objective, batching, residency and a payload ceiling of 64 KB. - The fabric (`platform.*`, first-party): `resource-offer@1` (a node's or provider's capabilities, multidimensional capacity, latency, health, pricing), `workload-requirements@1`, `placement-result@1` (the explain answer), `placement-plan@1` (signed, expiring route plans), `rate-card@1` (a provider price in its real unit, with its free allowance, source and verification date), `provider-state@1` (usage, forecast, reserve), `cost-snapshot@1` (with counterfactuals), `capacity-snapshot@1`. - The resource index: `common.resource-summary@1` and `common.resource-list-result@1` (GET /api/v1/resources in every service); `common.resource-cost@1` (a charge in weighted cost units with an idempotency key). - Fixtures for each, and the regenerated OpenAPI and JSON Schema bundle. ## 0.76.0 — 2026-09-28 **The node registry** (ADR-034 section 12; roadmap WS-X1). - `network.node@1`: one machine of the platform as the registry lists it: its roles (web, app, data, media-worker, ingest, edge-probe, edge-relay, edge-cache, gpu, staging, ci), region-level location (region, country, optional city and coordinates), provider, an HTTPS beacon clients time for their round trip, and health. No addresses, capacity or secrets are public. - `network.node-report-request@1` and `network.node.report` (internal, held by Host): `POST /internal/nodes/report` sends the complete set of nodes one inventory knows. A node absent from a later report of the same source is marked down, never silently deleted. - `network.node-list-result@1`: `GET /api/v1/nodes` (public, `?role=`, `?region=`), the input of openvibe-sdk/geo's `nearest()`. ## 0.75.0 — 2026-09-28 **Per-streamer AI budgets are AI quotas** (roadmap WS-O task 2). - `ai.quota.attribution.manage` (first-party): a service caps what runs attributed to one of its own entities may cost or request per hour or day (`PUT /api/v1/attribution-quotas/{attribution}`, `ai.attribution-quota-put@1`, optionally only for workflows under a prefix). It reads the cap with what the window has used (`ai.attribution-quota@1`) or removes it. The attribution's service must be the caller: Live caps `live:user:<id>`, a streamer's daily AI-viewer budget. A run over the cap is refused 429 `quota.exceeded` before any provider is called. ## 0.74.0 — 2026-09-28 - `ai.credential-put@1`: `api_key` is optional after the first put. The stored key is kept for a change of models or budget, but must be entered again to change `provider` or `base_url`, so a key never follows a new endpoint on its own (400 `credential.key_required` otherwise). Roadmap WS-O task 2. ## 0.73.0 — 2026-09-28 **A person's own provider key lives in OpenVibe.AI** (roadmap WS-O task 2). - `ai.credential.manage` (first-party): the service that holds a person's consent (Live, for a streamer's AI viewers) stores their key at `PUT /api/v1/credentials/{subject}` (`ai.credential-put@1`: provider openai or anthropic, an https `base_url`, the key, models per role, and an optional `budget_usd_per_day`). It reads it back without the key (`ai.credential@1`, a four-character hint and today's spend) or deletes it. Only the storing service uses or changes a credential. - `ai.run-request@1` gains the optional `credential { subject }`. The run calls that provider with that key only: no fallback, never cached, the shared paid budget untouched, and the credential's own daily budget enforced. Chat operations only. ## 0.72.0 — 2026-09-27 **Mod principals in Network** (roadmap WS-M task 3, ADR-013). - `mods.grant.manage` is active. A runtime (OpenVibe.Games) registers an install's principal `mod:<mod_id>` (`POST /internal/mods`, `network.mod-install-request@1`: the manifest, and the subset staff approved at install; the rest stays pending). It then approves or revokes one capability (`POST /internal/mods/{mod_id}/grants`, `network.mod-grant-change@1`) or revokes the install (`POST /internal/mods/{mod_id}/revoke`). It reads them with `GET /internal/mods[/{mod_id}]`. Only the owning runtime or Network staff (`staff.games.manage`) change a mod. - `network.mod-principal@1`: the principal with requested, approved, pending and revoked capabilities, and a revision. - `network.mod.grants_changed@1` (internal): after every change, the complete approved set (by revision), the change, and whether the runtime or staff made it. The owning runtime sets its copy to it. Games lists it as consumed. ## 0.71.0 — 2026-09-27 **Account export and deletion** (roadmap WS-B task 7, ADR-033). - `network.account.export_requested@1` (internal): a person asked for a copy of their data. Each service that keeps data about people pushes its part to `POST /internal/account-exports/{export_id}/parts` (`network.account-export-part@1`: JSON files of the subject's own rows, at most 20 MB) with the new capability `network.account.export.contribute`, before the deadline (30 minutes). - `network.account.deleted@1` (internal): the 30-day grace ended and Network erased what it owns. Each service erases its rows once per `deletion_id`, as follows: - authored content goes, except items with others' replies, which stay as authorless tombstones; - votes go, and counts are recomputed; - money rows stay, pseudonymised, and held media stays. It then confirms at `POST /internal/account-deletions/{deletion_id}/confirmations` (`network.account-deletion-confirmation@1`, counts only) with `network.account.deletion.confirm`. `aliases` names subjects merged into the account earlier. - `network.account-export@1` (the job the person sees: pending, ready, partial, expired or failed, with each service's state), `network.account-deletion@1` (scheduled, cancelled or deleted), and `network.account-data-receipt@1` (Network's answer to a part or confirmation). - Live, Chat, Community, Media and Games list both events as consumed. ## 0.70.0 — 2026-09-27 **Account merge, the revocation reason and module policy** (roadmap WS-B task 5, ADR-029). - `network.user.token_valid_after` gains the reason `account_merged`: a folded-in account's tokens end when it merges (its sessions moved to the survivor). Consumers treat every reason the same. - `modules.namespace@1`'s description follows ADR-029. When both accounts have a record in a namespace, the survivor keeps its own and gains only the top-level fields it lacks; its values always win. Before, the absorbed record was simply dropped. ## 0.69.0 — 2026-09-27 **Account merge** (roadmap WS-B task 5, ADR-029). - **`network.subject.merged@1`** (event, first-party, visibility internal): `{ merge_id, from, into, merged_at, initiated_by, split_until? }`. `from` is now an alias of `into`. Network has moved what it owns (providers, sessions, OAuth grants, developer projects, OpenCoins, user modules) in the transaction that queues it. Each service repoints its own rows from `from` to `into`; a row it cannot move keeps the survivor's and drops the other, counted. Apply once per `merge_id`. Listed in Network's manifest `eventsProduced`. - **`network.account-merge-result@1`**: the answer of a merge, what Network moved as counts, and `replayed` for a retried merge. - **Staff map 1.2.0: `staff.identity.merge`** (owner, since 1.2.0): an account-recovery merge with a written reason and an audit row. People merge their own accounts without it, signed in to both. An owner token issued from map 1.1.0 holds it by role. ## 0.68.0 — 2026-09-26 **Creator analytics from events** (roadmap WS-E task 6). - **`live.stream.ended` gains an optional `stats` object:** peak and average viewers, unique chatters, messages and watch minutes. It holds counts only, never who. - **New schema** `network.creator-analytics-result@1`: a creator's daily and per-stream analytics, built on Network from `live.stream.ended`. - **New capability** `network.analytics.creator.read` (first-party): the full figures for Live's creator dashboards. Streams, minutes and peak viewers are public. ## 0.67.0 — 2026-09-26 **Products write follows on a person's behalf** (ADR-030 step 4). - **New capability** `network.follows.write` (first-party, never for apps): PUT and DELETE `/internal/follows/:type/:target` on Network, with the follower named in the request. - **New schema** `network.follow-write-request@1` (`follower`, `notify_email?`, `notify_push?`). Live's follow buttons use it, and Live keeps its table as a projection of `network.follow.*`. ## 0.66.0 — 2026-09-26 **Incidents and maintenance on the status page** (roadmap WS-N task 12). - **New schemas** `network.status-incident@1` (an incident or maintenance window: kind, title, severity, state, services, start and end, updates), `network.status-incident-list@1` (public: `active` and the last 30 days' `recent`) and `network.status-incident-request@1` (open one, or add an update). - **New capability** `network.status.incident` (internal, never for apps). It covers POST /api/v1/status/incidents and /:id/updates, for staff admins and the Host principal (`ovhost incident`, `ovhost maintenance`). - ADR-032 (containers for platform services) is accepted: services stay systemd units. ## 0.65.0 — 2026-09-26 **The follow graph on Network** (roadmap WS-E task 4, ADR-030). - **New event types** `network.follow.created` and `network.follow.deleted` (subject visibility for the follower). Their payloads carry `follower`, `target_type` (`channel` today: a Live channel named by its owner's subject), `target_id`, the notify flags, a per-pair `revision`, and for a delete a `reason`. Products keep projections they can rebuild. - **New schemas** `network.follow-status-result@1` (the public follower count, plus the caller's own follow when signed in) and `network.follow-list-result@1` (a page of follows, never public). - **New capability** `network.follows.read` (first-party, never for apps): who follows a target, for notifications and projections. ## 0.64.0 — 2026-09-26 **Wiki VIP spaces and pages** (roadmap WS-K task 8). - **`vip` joins Wiki's visibilities** (between `members` and `private`). It covers `wiki.space@1`, `wiki.page@1`, `wiki.space-write-request@1`, `wiki.page-write-request@1` and the `wiki.page.updated`, `wiki.page.published`, `wiki.page.unpublished`, `wiki.page.deleted` and `wiki.space.updated` payloads. - **Who reads it:** a `vip` space or page is read by the space's roles and by the viewers OpenVibe.VIP admits as members of the space owner. OpenVibe.Wiki asks `vip.resource.policy.evaluate` with the resource `wiki/page/<id>` when the page itself is VIP-only (else `wiki/space/<id>`), and the fallback `{ requirement: 'member', binding: 'wiki:gated_page' }`. - **Where it never appears:** in search, sitemaps or feeds (its index document is a tombstone, like `members`). - **Official spaces** have no VIP owner, so Wiki refuses `vip` there. - Widening only: every earlier value stays valid. ## 0.63.0 — 2026-09-26 **Project usage rollups** (roadmap WS-N task 4, ADR-014): what feeds the per-project dashboards on OpenVibe.Codes. - **New `common.usage-recorded@1`**: one closed window (an hour or a day) of one developer project's use of one capability in one environment, as the owning service counted it: `project_id`, `env`, `capability`, an optional `dimension` (a job type or tool id), `unit`, `window`, `window_start`, `window_end`, `quantity`, `errors`, `error_codes` (up to 20) and `samples` (up to 10 recent failures: time, code, status, trace id and a job, event, run or object id), plus `revision`. Totals, not deltas: a later rollup with the same key replaces the earlier one. It never names who did the work (no subject, app, address, session, input or message), and no producer emits one per request. - **New event types `tools.usage.recorded`** (a Tools satellite's hour of a project's jobs, unit `jobs`, per `tools.job.create` or `tools.tool.run` and job type or tool) **and `events.usage.recorded`** (Events: `events.app.publish` in `events`, refusals as errors; `events.app.subscribe` in `deliveries`, failed webhook attempts as errors). Both payloads are `common.usage-recorded@1`; envelope subject `{ type: project, id: <project_id> }`, visibility `internal`, priority `low`. The Tools and Events manifests produce them; Network consumes both. - **New `network.project-usage-result@1`**: what `GET /api/v1/projects/:project/usage?days=&env=` on Network answers a project's owner, admins and staff: daily rows and range totals per service, capability, unit and environment, the project's recorded quotas with what their current window used, and errors by code with the recent sampled failures. - `npm test` checks that each `<service>.usage.recorded` is the common payload, owned by its service and consumed by Network, and that a rollup carries no identity and at most ten samples. Additive. ## 0.62.0 — 2026-09-26 **Operator alerts** (roadmap WS-H task 11; the delivery path that Host's Prometheus alerts lacked). - **New capability `network.operator.alert`** (internal, owner network), implemented by `POST /internal/operator/alerts` on OpenVibe.Network and granted to the Host principal. The request is the complete set of alerts firing now at one source (`prometheus`). Network pages the operator (the owner account) as a critical or high `admin` notification when an alert opens, reminds once a day while it stays open, and sends a resolved notice when it drops out of the set. Being an `admin` notification, it reaches the bell, web push and the realtime topic, and no block hides it. - **New schemas** `network.operator-alerts-request@1` (source, sent_at, up to 200 alerts: fingerprint, name, severity, summary, description, service, started_at; no free-form labels) and `network.operator-alerts-result@1` (firing, opened, reminded, resolved, notified). ## 0.61.0 — 2026-09-26 **A person's realtime topic and realtime tickets** (roadmap WS-E task 3, WS-F task 1; ADR-005 amendment 2). - **New event type `network.notification.created`**, payload `network.notification.created@1`. Network writes it to its outbox in the transaction that stores a notification. Envelope: subject `{ type: user, id: <recipient usr_> }`, visibility `subject`, actor `system:network`. The payload is the id, type, category, priority, service, `created_at` and the recipient's unread count; never the title, message, link or sender. The Network manifest produces it. "`user:<id>`" in the roadmap is this event type plus subject visibility: a browser subscribes to `network.notification.*` and receives its own events only. - **New `identity.realtime-ticket-claims@1`**: the two-minute, single-use RS256 ticket with which a browser opens Events' `/realtime/stream?ticket=…`. Its claims are `iss <network issuer>/realtime`, `sub <usr_>`, `aud [openvibe.events]`, `typ realtime`, `purpose realtime`, `iat`, `exp` and `jti rtk_…`. Three rules each keep it from passing for a session: the issuer, `typ` and the audience. - **New `network.realtime-ticket-result@1`**: what `POST /api/v1/realtime/ticket` answers. That is the ticket, `expires_at`, `expires_in` (at most 300), `stream_url`, `topics` (Events patterns only, never `user:…`) and `subject`. - The Events manifest's notes name the ticket. There are fixtures for all three contracts, and `npm test` checks that the event never carries notification text or the sender, and that a ticket never has a session's issuer or claims. Additive. Also in this release, ADR-007 amendment 2026-09-26: SQLite recorded as the reviewed store for every service, with Events', Chat's and Billing's measured production load and the thresholds that would move a service to PostgreSQL (WS-S task 1). Documentation only. ## 0.60.0 — 2026-09-26 **Release manifest 1.2.0** (`registry.release-manifest`, roadmap WS-P task 7, ADR-016 amendment 2). Three optional fields: - `client_generation`: the generation of the client this release serves. - `min_client_generation`: a tab below it reloads at the next safe moment (reason `required`). - `shell`: `{ version, components }`. A tab whose shell differs is prompted, never updated in place. Fixtures cover a full 1.2.0 manifest, a negative generation and a bad shell version. Additive. ## 0.59.0 — 2026-09-26 **Media namespaces and grants** (roadmap WS-G task 2). Media's grants are five verbs, each one capability checked per namespace: read (`media.object.read`), list, write (`media.object.upload`), delete and transform (`media.derivative.create`). - **New capabilities**, public and active, namespace-scoped: `media.object.list` (object, job, namespace and v1 file lists; answers `media.object-list@1`) and `media.object.delete` (soft delete, restore, v1 file delete; answers `media.object@1`, announces `media.object.deleted`). The Media manifest lists them. Media keeps accepting the older ids for them: `media.object.read` lists and `media.object.upload` deletes and transforms, unless a namespace's policy is `strict_verbs`. - **New contracts** `media.object@1` (an object of the v2 object API, with its `namespace`) and `media.object-list@1` (a page of the object list), with fixtures. - `media.derivative.create` stays planned: its description now says Media accepts it as the transform verb (checked for the job's object's namespace), and it lists the cancel route. `media.lifecycle.read` and `media.lifecycle.transition` say which verbs guard their routes today. - `identity.service-token-claims@1` (description only): a developer app token's `ns` carries its `project_id` and `app.<project_id>.*`; on Media `app.<project_id>` and `app.<project_id>.sandbox` are the project's namespaces. New valid fixture `app-project-namespaces`. Additive. ## 0.58.0 — 2026-09-26 **Release notifications** (roadmap WS-P task 9, ADR-016 amendment 1). A new event type, **`host.release.published`**, with the payload contract `host.release.published@1` (catalog visibility public). It means a network service's release went live. - **Who publishes it.** OpenVibe.Host's operator plane: `ovhost deploy|rollback`, and `ovhost announce` for services deployed by their own scripts. - **Envelope.** Subject `{ type: release, id: <service>:<release> }` and visibility public. Signed-out browsers get it over Events realtime, and openvibe-shared release-watch (1.17.0, `topics=host.release.published`) checks `/release.json` at once. - **Payload.** `service`, `release` (the `/release.json` release id, with the release manifest's pattern), `commit` or null, `origin` (an http(s) origin) or null, `deployed_at`, and optionally `components` (kind and version only, at most 32) and `rollback: true`. Identifiers only. `host.deploy.activated` also gets its payload contract, `host.deploy.activated@1` (first-party), which is what Host's Stage B already emits for a tenant site's activation: `project_id`, `site_id`, `site`, `deploy_id`, `previous_deploy_id`, `rollback`. It has subject `deploy` and visibility internal. The two types never share a payload, and fixtures check that neither passes for the other. The host manifest lists `host.release.published` in `eventsProduced`. Additive. ## 0.57.2 — 2026-09-26 **Three decisions** (documentation only): ADR-029 account merge (WS-B task 5), ADR-030 follow graph (WS-E task 4), ADR-031 S3-compatible surface for Media (WS-N task 8). Each is accepted with its acceptance tests; the implementations follow in Network, Live and Media. ## 0.57.1 — 2026-09-26 **ADR-024 amendment 1** (roadmap WS-E task 2): the theme preference stays Network's own data (not a user module); community themes are reviewed before they are public; theme names and values are allow-listed (shared tokens; colours, numbers, lengths and shadows only); themes import and export as `openvibe-theme@1` files. Documentation only. ## 0.57.0 — 2026-09-26 **Chat rooms on the realtime plane** (roadmap WS-I task 9). Two event types OpenVibe.Chat produces, with payload contracts: - `chat.room.message.created` — a message in a **public** room, visibility public, so browsers subscribed to `chat.room.*` get it over Events realtime (replay capped by the public window). Private rooms and DMs never emit it; they stay on Chat's WebSocket. - `chat.room.message.deleted` — ids only, with `redacts` (subject_type `chat_room_message`), when a public room's messages are deleted or the room turns private or is archived, so the text stops being replayable. ## 0.56.1 — 2026-09-26 **ADR-005 amendment 1: presence** (roadmap WS-F task 2). Presence is ephemeral and lives in Chat's delivery plane: room user lists over Chat's WebSocket, and `GET /api/chat/online?users=` for other products; no Events topic carries it, and people hidden by `chat.presence_prefs` (or whose preference is unknown) read as offline. `test/presence-policy.test.js`: no presence event type in any manifest. Documentation and a test; no schema change. ## 0.56.0 — 2026-09-26 **Loyalty summary and the loyalty-is-never-money test** (roadmap WS-K task 9). - New user-module namespace `live.loyalty` (owner live, private): `channel_points_total`, the top ten `channels` by points (`channel`, `points`), `arena_level`, `arena_xp`, `computed_at`. A summary Live writes; Live's `channel_points` and `arena_trash_levels` stay the truth. - `test/loyalty-policy.test.js` (run by `npm test`): loyalty namespaces are private and carry no money-like field; no `network.coins.*` or loyalty capability withdraws, cashes out, converts or prices loyalty; no `billing.*` capability takes or pays loyalty; the OpenCoins request bodies carry no price. The OpenCoins transfer grant stays revoked in Network (`REVOKED_GRANTS`, ADR-012 rule 5). ## 0.55.1 — 2026-09-26 **Lifecycle declarations follow the shutdown and fencing fixes** (WS-P task 1 follow-ups). Manifest text only; no schema change. - `tools`: every process, the gateway included, stops through a shared graceful stop: timers, pollers and job workers first, then the HTTP drain (4 s, Connection: close, event streams closed), usage flush, databases and pools, exit 0 within the 5 s already declared. The note about the gateway having no SIGTERM handler is gone. - `games`: `deadlineSeconds` 0 → 10 (the unit allows 30). The stop now refuses new connections and upgrades, stops the simulation and timers, closes every game and editor socket with 1012 after announcing the restart (each close saves that character), saves the world, lets requests finish, awaits the mirror, the progress summaries and the outbox, then closes world.db. - `community`: the Pulse subscription retries, the profile and search-document scans, the Discord relay and the events outbox are now stopped (the work in flight awaited) before community.db closes, within 5 s. - `media`: `leases.claims[0].fencing` is a real fence now: a claim's random `lease_token`, matched by renew, checkpoint, succeed, fail and cancel; a stale holder is refused and counted (`media_job_stale_completions_total`). - `network`: the timers, pollers and the event relay are stopped on SIGTERM, analytics and network.db are closed; requests in flight get Connection: close (8 s at most). - `ai`: the ai.run.* outbox relay is stopped after the runs drain, before the database closes. ## 0.55.0 — 2026-09-26 **Lifecycle declarations** (roadmap WS-P task 1). `registry.service-manifest@1` gains an optional `lifecycle` block, and every one of the 31 manifests fills it from its service's code and systemd units: - `liveness`: the endpoint (the manifest's `health`) and what a 200 from it proves; - `shutdown`: `signal`, `deadlineSeconds` (the longest the process takes from the signal to exiting, as its own forced-exit timer bounds it), `drains` in order, optional `workers` (worker units that drain on their own, OpenRe's) and a `note` for gaps (the Tools gateway has no SIGTERM handler; Games and Community drain almost nothing); - `startupRecovery.resumes`: what the next start picks up, each as outbox, jobs, sessions, consumer, schedule or state; - `rollback`: `conditions` (automatic and manual), `window` and `blockers` (forward-only migrations, table rebuilds, authority switches); - `contracts.range`: the openvibe-contracts versions accepted, the same as `contractRanges`; - `leases.claims`: what is claimed, by whom, when it lapses and how a stale holder is fenced. A part that does not apply is `{ "none": "<why>" }` (libraries, the Examples repository, retired Realtime, static Sites). `npm test` checks that every manifest declares all six, that a running service's liveness endpoint is its `health` and its shutdown is concrete, that a library declares none for liveness, shutdown and recovery, that `contracts.range` matches `contractRanges`, and that every lifecycle field has a description. `ovhost validate` (OpenVibe.Host) refuses a host service whose lifecycle lacks a field or whose deadline exceeds its unit's TimeoutStopSec. Additive. ## 0.54.0 — 2026-09-26 **`media.moderation.action`** (ADR-022, WS-D task 1): Media's staff actions on someone else's media, which are retention holds placed or released and staff deletions or visibility changes. The payload is common.moderation-action@1, produced by media and consumed by network. ## 0.53.0 — 2026-09-26 **Moderation audit events for ten more producers** (ADR-022, roadmap WS-D task 1). **`common.moderation-action@1`** (owner network, public) is one staff or moderator action on someone else's content or account, modelled on `community.moderation.action`: `action` (a short verb id such as `post.hidden`), `target { type, id, owner_subject? }` and `actor_subject` are required; `reason` and `details` are optional; it never carries the content. Events requires an event's prefix to match its source, so each producer gets its own type, a thin schema over the shared one: **`tools.moderation.action`**, **`games.moderation.action`**, **`wiki.moderation.action`**, **`blog.moderation.action`**, **`news.moderation.action`**, **`reviews.moderation.action`**, **`deals.moderation.action`**, **`coupons.moderation.action`**, **`trade.moderation.action`** and **`codes.moderation.action`**. Each service manifest produces its type, and the network manifest consumes all ten into the moderation audit log. Fixtures for each; `npm test` checks that every one is the shared payload and that Network consumes every `*.moderation.action`. Additive. ## 0.52.0 — 2026-09-26 **`common.config-snapshot@1`** (roadmap WS-C task 7, the configuration model): one immutable revision of one service's configuration namespace (`live.site_settings`, `network.site_settings`, `media.storage_tier`), as `openvibe-shared/config` keeps it and `/api/admin/config` shows it. It carries service, namespace, a revision that grows per namespace and the previous one, a state (`proposed`, `active`, `superseded`, `rejected`, `rolled_back`), the values with every secret-class key as `{ redacted: true, fingerprint }` (an HMAC-SHA256 of the value under a secret key the service keeps per namespace: equal fingerprints within a namespace mean the value did not change, and the value cannot be recovered or guessed offline without the key), a `public`/`internal`/`secret` classification per key, a sha256 checksum over the canonical JSON of the values as shown (secrets as their markers, so anyone can check it and it reveals nothing more), who created and activated it and when, a reason, the rejection error, and `copied_from` for a rollback. A rejected revision names its error; an active, superseded or rolled-back one names when it became active. Fixtures for an active revision with a redacted secret, an imported first revision, a rejected tiering change, a rollback and a proposal; an unkeyed `sha256` marker is invalid. Additive. ## 0.51.0 — 2026-09-25 **OpenAPI 3.1 per service** (roadmap WS-C task 6): `generated/openapi/<service>.json` for each of the 24 services that own an active capability, 450 operations in all, plus `generated/openapi/index.json`. `lib/openapi.js` (`openapi.buildOpenApi()`, `openapi.index()`, `openapi.document(id)`) builds them from each capability's `implementedBy` routes and its input and output schemas: - one operation per route, listing the capabilities it performs (`x-openvibe-capabilities`, visibility and quota class), with a bearer token as its security; - a JSON, multipart or binary request body, or query parameters on reads; - a `2XX` response from the output schema, and `application/problem+json` errors; - every schema reached, as a component. The documents are validated as OpenAPI 3.1 in `npm test` (`test/openapi.test.js`, dev dependency `@seriousme/openapi-schema-validator`), and `generate --check` keeps them current. ## 0.50.0 — 2026-09-25 Capability schemas (roadmap WS-C task 4), starting with Search: **`search.query@1`** (the query string of `GET /api/v1/search` and `/suggest`: q, owner, type, lang, facets, limit, cursor, `facet.<key>`), **`search.query-result@1`** (a page of hits the caller may see, `next_cursor`, facet counts; never an ACL or a total) and **`search.write-result@1`** (`PUT /api/v1/documents/…`: applied or unchanged; stale and conflicting revisions are 409 problems). `search.query.run` and `search.query.delegate` name the first two; `search.document.write` names the third as its output. Fixtures for each. Two conventions so every capability can name schemas: **`common.no-body@1`** (a request whose parameters are the path and query) and **`common.binary@1`** (a response of bytes); **`media.file-upload@1`** is the multipart body of `POST /api/v1/:app/files`. The Media and Tools capabilities name them. **A ratchet**: `test/capability-schemas.test.js` fails on an active capability without an input or output schema unless it is in `compatibility/capability-schema-gaps.json` (162 today), and on a listed gap that has been filled; the list only shrinks. Additive. Every active capability now names both schemas: **the gap list is empty (162 → 0; 178/178 active capabilities complete)**. 275 new request, result and record schemas, each read from the owning service's routes, with valid and invalid fixtures. By owner: **chat** (2: the `/ws/chat` chat frame and its broadcast, the Live bridge batch), **community** (10: paste record, paste create/write/moderate, forum post writes, discussion moderation, Pulse writes), **billing** (19: transaction, balance, cashout, subscription, entitlement and intent records with each capability's request and result), **ai** (7: provider and model records, provider management, usage, template/workflow/route versions), **events** (8: publish result, pull reads and checkpoints, subscriptions, delivery admin), **network** (9), **vip** (26), **tips** (25), **live** (6), **games** (5, including the mod runtime bindings for props and announcements), **openre** (13), **sources** (2), **wiki** (18), **blog** (20), **codes** (2), **host** (10), **news** (24), **reviews** (23), **deals** (15), **coupons** (12) and **trade** (19). Where one capability covers several routes with different bodies, its schema is an `anyOf` of one branch per body. Requests set `additionalProperties: false` only where the service refuses unknown fields. Capabilities whose routes all take no body name `common.no-body@1`. Additive. Seven older capability schema references are corrected to what the routes take and return. `events.event.read` now answers `events.read-result@1` (was the bare envelope). `codes.release.read` answers the new **`codes.release-read-result@1`** (was `codes.app-manifest@1`). `community.paste.create` answers the new **`community.paste-create-result@1`**. `community.post.create` answers the new **`community.post-write-result@1`**, built on the new **`community.forum-thread@1`** and **`community.forum-post@1`** records (both were `common.entity-ref@1`). `community.pulse.write` takes the new **`community.pulse-write-request@1`** (was `common.entity-ref@1`). `games.mod.manage` takes the new **`games.mod-manage-request@1`**, and `games.mod.read` answers the new **`games.mod-read-result@1`** built on the new **`games.mod-view@1`** (both were `mods.mod-manifest@1`). No contract changes shape; only the capability manifests point elsewhere. Additive. The remaining references that did not match their routes are corrected the same way (29 new schemas): - **codes.release.manage** takes **`codes.release-manage-request@1`** (was `codes.app-manifest@1`: the body wraps the manifest with kind, notes and publish; deprecate and revoke take a reason). - **community.pulse.read**, **community.space.read** and **community.thread.read** answer **`community.pulse-read-result@1`**, **`community.space-read-result@1`** and **`community.thread-read-result@1`**, with the new **`community.pulse-item@1`** and **`community.forum-space@1`** records. Each also covers its HTML pages and RSS feeds. All three were `common.entity-ref@1`. - **community.comment.write** takes **`community.comment-write-request@1`** and answers **`community.comment-write-result@1`**, with the new **`community.comment-thread@1`** and **`community.comment@1`** records. Both were `common.entity-ref@1`. - **sources.item.read** and **sources.source.read** answer **`sources.item-read-result@1`** and **`sources.source-read-result@1`**: one branch per route, lists and single records. - **ai.run.read** answers **`ai.run-read-result@1`**. **ai.run.create** takes **`ai.run-create-request@1`** (a run request, a direct operation's input, citations, or no body) and answers **`ai.run-create-result@1`** (`{ run }` or `{ citations }`, not the bare run), with the new **`ai.run-citation@1`**. - **events.event.publish** and **events.app.publish** take **`events.publish-request@1`**: one envelope, or `{ events: [...] }` of 1 to 100. - **identity.subject.resolve** takes **`identity.resolve-request@1`** and answers **`identity.resolve-result@1`** (**`identity.subject-projection@1`**, or `{ results }` for a batch). They were `identity.legacy-identity-map@1` and `identity.subject-ref@1`. - **network.modules.write** takes **`network.module-write-request@1`** (`{ data }`; the answer stays `modules.module-record@1`). - **media.object.read** takes no body and answers **`media.file-read-result@1`** (the file's metadata, or its bytes on `/f/:key`). **media.object.upload** answers the new **`media.file@1`** (Media answers a file record, not a `media.media-ref@1`). - **search.query.run** and **search.query.delegate** answer **`search.read-result@1`**: search, suggestions, or one document. **search.document.write** takes **`search.document-write-request@1`** and answers **`search.owner-result@1`**: write results, reconciliation pages, the stored document, rejections. - **tools.tool.read** answers **`tools.tool-read-result@1`** (the list, one tool, or its schema document). **tools.job.create** takes **`tools.job-create-request@1`**: retry and references take no body. **tools.job.read** answers **`tools.job-read-result@1`**: the job, or a result file's bytes. The Tools assertions in `test/run.js` name the new ids. No existing contract changes shape; only the manifests point elsewhere. Additive. ## 0.49.0 — 2026-09-25 Platform blocks (roadmap WS-E task 5). **`network.block.changed`**: a person blocked or unblocked someone (`blocker`, `blocked`, `active`, a per-pair `revision`); Network keeps the blocks, keyed by subjects, and Chat and Community consume the event. New capability **`network.blocks.read`** (first-party, never for apps): `GET /internal/blocks?subject=` lists who a person blocked and who blocked them. Fixtures for a block and an unblock. Additive. ## 0.48.0 — 2026-09-25 **Correction to 0.47.0.** `network.grant.changed` was already emitted by Network's developer event relay for a developer app's grant, and OpenVibe.Events acts on it (it stops an app's subscriptions when its `events.app.subscribe` grant leaves approved). 0.47.0 gave that event a service-principal payload that does not match. Now `network.grant.changed` describes what is actually sent: `{ project_id, capability, audience, from, to }`, subject `app`. A first-party service principal's grant change is the new **`network.principal_grant.changed`** (the 0.47.0 payload under its own name). Nothing consumed the 0.47.0 shape. The compat gate records this in `compatibility/corrections.json` (accepted against v0.47.0 only). ## 0.47.0 — 2026-09-25 **`network.grant.changed`** gets a payload contract (roadmap WS-D task 3; corrected in 0.48.0: that shape is `network.principal_grant.changed`): a service principal's capability grant was given, changed (namespaces or expiry), revoked by the owner, or expired, with actor and reason. Network writes it beside an audit row in the transaction that changes the grant; the seeded defaults are not announced. New capability **`network.staff.read`** (active, first-party, never grantable to apps): list the network's staff with their roles and staff-map capabilities, `GET /api/v1/staff/moderators` (WS-D task 4). Fixtures for a grant and an expiry. Additive. ## 0.46.0 — 2026-09-25 **`live.moderation.action`** (ADR-022, roadmap WS-D task 1): a staff or channel-moderator action taken on OpenVibe.Live outside chat (site, global and IP bans, message deletes and purges from the admin panel, a stream force-ended, relay users hidden, channel moderators added or removed), with the same payload as `chat.moderation.action`. Events requires an event's prefix to match its source, so Live cannot send Chat's event for its own admin actions. OpenVibe.Network consumes it into the moderation audit log. The live manifest produces it and the network manifest consumes it. Fixtures cover a site ban and a force-ended stream. Additive. ## 0.45.0 — 2026-09-25 **`live.index_document.upserted|deleted`** also carry Live's public VOD and clip pages (types `vod` and `clip`, id = the Media id, canonical `openvibe.live/vod/<id>` and `/clip/<id>`), alongside channels. Live owns those canonical pages (title, channel, AI overview, transcript), so Live sends their Search documents. `media.index_document.*` is only for VODs and clips whose canonical page is on openvibe.media (apps other than Live), the same rule as Media's sitemap. Fixtures for a VOD, an AI clip (noindex) and a VOD tombstone. Additive. ## 0.44.0 — 2026-09-25 Search documents from three more owners (roadmap WS-O task 10): **`live.index_document.upserted|deleted`** (a streamer's channel page, type `channel`), **`media.index_document.upserted|deleted`** (public, playable VODs and clips, types `vod` and `clip`) and **`community.index_document.upserted|deleted`** (public forum threads and public pastes, types `thread` and `paste`). They use the same shape as the Wiki, Blog, News and other owners' index events: the `search.index-document@1` document, or a tombstone `{ type, id, revision }`. OpenVibe.Search consumes them through `*.index_document.*`. The live, media and community manifests list them. Fixtures cover the Live payloads. Additive. ## 0.43.0 — 2026-09-25 **`network.user.updated`** (roadmap WS-B task 2). A person's whole current profile after any change that other services may know about: username, display name, picture, colour, role or ban. The payload carries a `revision` and the list of what `changed` (`created` for a new account). Consumers keep a projection by writing the newest over what they have: roles apply in both directions, and a ban arrives even for someone who never comes back. Network produces it and Live consumes it (Live's `subject_projection`, replacing the `/internal/user-role` push). Fixtures cover valid and invalid payloads. Additive. ## 0.42.0 — 2026-09-25 The service manifests carry what Network's registry and site list used to hard-code (roadmap WS-C task 1, "manifest-derived registry"): - **`internalOrigin`**: the loopback address a running service answers on, which the registry polls. An `OV_<ID>_INTERNAL_URL` loopback override can still replace it. - **`exposure`**: where the service can be reached today. `state` is live, internal, library, repository, placeholder or retired; `publicSite` says what the public domain answers (the service, a placeholder, or nothing); there is an optional `note`; libraries also give `package` and `repo`. This is separate from `status` (maturity). - **`site`**: how a site is presented, with `name`, `icon`, `tagline`, `what`, `legalProfile` (the legal wording of /terms, /privacy, /dmca), `position`, and `host` for a site without a public origin. A service without `site` is not a site. - **`ready`** is added to the network, media and games manifests (`/api/ready`). The values are exactly what Network hard-coded. A service going public is now one manifest edit here. The test requires every first-party manifest to have an exposure, every running service to have a unique loopback port, libraries to name their package and repository, and site positions to be unique. The additions are optional in the schema, so this release is additive. ## 0.41.1 — 2026-09-25 Plain-language descriptions for `ai.preferences`, `chat.preferences`, `games.progress.summary` and `live.profile`. my.openvibe.network now shows these descriptions to people on its AI & Data tab. No schema change. ## 0.41.0 — 2026-09-25 User modules, the rest of D06 (roadmap WS-B task 9): - **Field-level read rules.** A namespace may list `readers`: `{ service: [fields] }`. A service other than the owner that reads a record (with `network.modules.read`) sees only the public fields and the fields listed for it. `modules.serviceView(namespace, service, data)` applies the rule; Network's service read applies it. - **Version migration.** A namespace may list declarative `migrations` (`{ from, to, rename, drop, defaults }`). `modules.upgrade(namespace, data, version)` brings a stored record to the current version. Network upgrades a record when it is read and stores it at the current version on the next write. The contract test requires a path from every earlier version. - **New namespaces:** - `chat.dm_settings`: new conversations from everyone or nobody, group invites, previews; - `chat.presence_prefs`: whether the person is named in chat user lists; - `live.stats`: 30-day streaming summary; - `ai.usage_summary`: 30-day AI runs on the person's behalf; - `community.profile`: threads, posts, comments, pastes, first and last activity; - `wiki.projects`: spaces the person owns or edits, where only public ones are named. - **`chat.tts_defaults` v2**, now owned by Chat. Its fields are the settings Live's chat panel actually has: `send`, `send_while_live`, `volume`, `sounds`, `sound_volume`, `sources`. v1 (voice, rate, muted) was never written, and a v1 record upgrades to an empty v2 record. - **`tools.usage` v2** adds `favorites` (unique tool ids, at most 24). The person may write it; every v1 record is valid v2. - **`ai.preferences`** is readable by `ai` (all five fields). - The chat and ai service manifests list their new namespaces. ## 0.40.0 — 2026-09-24 The chat manifest names its own domain: **openvibe.chat** (`domains`, `publicOrigin`). OpenVibe.Chat serves the site itself (global chat, messages, settings, sign-in with the Network), and Network's first-party CORS list, which comes from these manifests, now includes it. Additive. ## 0.39.0 — 2026-09-24 **`network.user.token_valid_after`** (revocation propagation, roadmap WS-B task 4): when a person's tokens stop being good (a password change or reset, sign out everywhere, a ban, an account deletion or staff ending their sessions), Network moves their cutoff and emits this event in the same transaction. Every service that accepts Network user tokens refuses one whose `iat * 1000 < Date.parse(valid_after)` (Network's own rule), drops what it cached for those tokens and closes the sockets they opened. Consumers keep the latest cutoff per subject. Network produces it; Chat, Community, Live, Media, Tools and Games consume it. Fixtures cover valid and invalid payloads. Additive. ## 0.38.0 — 2026-09-24 Staff map 1.1.0 (ADR-022), so the content products and Games can check staff capabilities instead of comparing role names: - **`staff.content.moderate`** (global_mod): moderate what people publish on Blog, Deals, Coupons, Codes and Host. - **`staff.editorial.manage`** (admin): the network's own publications: the official blog and wiki spaces, News stories, Reviews entities, Trade context, Coupons merchants. - **`staff.games.manage`** (admin): the Games map editor and mod administration. - **`since` and the `staff_map` claim.** Capabilities record the map version that added them, and Network issues `staff_map` with `staff_caps`. `staff.can()` judges a capability newer than a token's map by the token's role, so an addition reaches existing tokens (which live for days) at once. What a token was issued still wins for everything its map knew. Additive. ## 0.37.0 — 2026-09-24 `network.integration.github.read` (first-party only) lets a service read the network's GitHub API token. The owner sets the token in Network's admin panel, or in `GITHUB_TOKEN`. OpenVibe.Blog's network changelog uses it, so its GitHub calls do not share the host's anonymous rate limit. It is never granted to apps or mods. Additive. ## 0.36.0 — 2026-09-24 The moderation audit log (ADR-022): - **`chat.moderation.action`** now has a payload contract, matching what OpenVibe.Chat already sends for chat and Live moderation. - **`community.moderation.action`** is new: staff actions on other people's content in Community. OpenVibe.Network's manifest consumes both, plus `tips.interaction.moderated` and `billing.staff.action`, for the audit log staff can read in admin. Fixtures cover valid and invalid payloads. Additive. ## 0.35.0 — 2026-09-24 - OpenVibe.Community publishes events: `community.paste.created|updated|deleted`, `community.thread.created`, `community.post.created`, `community.comment.created` (payload contracts with fixtures; the community manifest 0.2.0 lists them). Payloads carry ids, owner or author subject, visibility and the public URL, never a body, title or content: consumers (Search, Pulse, Live's Content feed) read the item itself. ## 0.34.3 — 2026-09-24 - `openvibe-contracts-check` skips `*.test.*` and `*.spec.*` files: tests mint tokens carrying other owners' capabilities on purpose, and repositories that keep tests next to the code (Games) could not run the check. ## 0.34.2 — 2026-09-24 - Fixture fix: the valid mod-manifest 1.1 example (`market-stall-1.1.json`) asked for the first-party `vip.entitlement.check`, which is never granted to apps or mods; it now asks for the public `vip.perk.list`. ## 0.34.1 — 2026-09-24 - `tools.tool.run` and `tools.net.probe` are **active**: the run API (`POST /api/v1/tools/:id/run`) and the gateway jobs facade shipped in OpenVibe.Tools 5ac8309 and are deployed. The SDK v0.6.0 tools client passes against them. - `tools.run@1` lists the problem codes the run API emits beyond 0.33.1's: tools.input.too_large (413), tools.run.timeout (504), tools.origin.refused (403), tools.session_required (401), tools.run.file_not_found (404), tools.run.failed (500). Additive; no schema change. ## 0.34.0 — 2026-09-24 Additive: `compat.js` reports no breaking change against v0.33.1. Closes the Contracts rows of the roadmap completeness audit (§28.3, §30.2, W1 D7, W12, W22). **51 event payload contracts**, all `active`: every one is emitted by its producer's code today. Each was read from the emitter and its call sites. News, Reviews and Deals were also checked against envelopes captured from their own code, and Trade against its one production outbox row. - News (11): `news.source.ingested|failed`, `news.cluster.updated`, `news.story.created|flagged|published|updated|unpublished|retracted`, `news.index_document.*`. - Reviews (9): `reviews.entity.merged|split`, `reviews.signal.added|removed`, `reviews.summary.published|updated|unpublished`, `reviews.index_document.*`. - Coupons (8): `coupons.coupon.created|updated|expired|disabled`, `coupons.report.created`, `coupons.confidence.changed`, `coupons.index_document.*`. - Deals (6): `deals.offer.created|updated|expired`, `deals.vote.changed`, `deals.index_document.*`. - Trade (5): `trade.observation.created`, `trade.source.stale|recovered`, `trade.index_document.*`. - Games (10): `games.player.joined|left`, `games.skill.leveled`, `games.blueprint.unlocked`, `games.world.saved`, `games.mod.installed|enabled|disabled|grants_changed|revoked`. - Media (2): `media.object.deleted` and `media.object.visibility_changed` (OpenVibe.Media 039dc48). Triggers stage them in the transaction that changes the object, and the outbox writes them there. They carry identity and state only: never the title, owner, storage keys or size. A visibility change never repeats the value. The media manifest now lists both. Every `*.index_document.upserted` is a `search.index-document@1` with the owner fixed. Every `.deleted` is a `{ type, id, revision }` tombstone. Community lists no `eventsProduced`, because it emits nothing yet, so there is no `community.*` contract. 29 listed events still have no contract: - chat: 3 - codes: 3 - host: 4 - media: `vod.*`, `clip.*`, `object.uploaded` and `storage.*`, 7 in all - network: 5 - openre: 7 Found while reading the producers (fixes belong in those repos): - **News drops some flag events.** `stories.js:238` sends `priority: 'normal'` for `source_updated` flags. The envelope allows only `critical|important|low`, so Events refuses them. - **Coupons can publish service notes.** A calling service's note is copied into the public event's `reason`. - **Deals under-reports changes.** On the importer paths, `changed` can leave out a `product_id` or `expires_at` change. **ADR amendments** (dated 2026-09-24): - **ADR-003, delegated authorization.** - Who may name the acting person, and the rule that a call gets the intersection of the app's grants, the approved scopes and the person's own rights. - Staff power and money are never delegated. Tokens last 5 minutes and cannot be refreshed. Audit names both the app and the person. - The approval UX in force today, and what Network still owes: the capability list on the chooser, stored authorizations, and connected apps with revoke. - **ADR-006:** - four placement classes: canonical, hot cache, asset origin, local scratch; - `local` as the first-class dev provider; - the infrequent-access tier deferred at 2,839 objects / 481 GB. - **ADR-007:** - PgBouncer transaction pooling; - replicas only for reads that tolerate lag; - SERIALIZABLE plus ordered row locks and 40001 retries on money paths; - Redis never authoritative. - **ADR-012:** - The roadmap's ledger names mapped to Billing's transaction types and legs: platform fee, creator earning, payout hold and release, compensating reversals. - OpenCoins stays in Network. This supersedes the Wave 8 line "Network's OpenCoins wallet becomes a Billing client". - Billing's `cashouts`, reversal transactions and VIP plans replace the roadmap's `payouts`, `refunds` and `plans` tables. - **ADR-013:** - Package signing and review are named as open owner decisions. - Mod monetization goes only through Billing and VIP primitives. - The manifest 1.1.0 fields (below). - **ADR-021, extraction decided.** There is no analytics service. Analytics stays per service through `openvibe-shared/analytics`, and the schema stays in Shared. The decision rests on measured volume: Live has 1.78M raw events in 30 days (58k a day, 502 MB). The ADR names the triggers for a revisit. - **ADR-022, the Wave 12 revisit.** The decision stands. The ADR names the trigger for the next revisit. **Realtime is `retired`** (ADR-005), not a placeholder. A retired manifest offers no domain, capability, event, namespace or health path. **Staff capability map** (W1 D7, ADR-022): a new contract, `policy.staff-role-map@1` (owner `network`), and the map in force, `manifests/policy/staff-roles.json`. - **Roles:** `user < streamer < global_mod < admin < owner`. `owner` is never a stored role: it is `role: admin` plus the `is_owner` claim. `user` and `streamer` hold no staff capability. - **32 `staff.<area>.<action>` capabilities**, each with the lowest role that holds it: - 12 for `global_mod`, including `staff.moderation.chat|calls|channels|bans|ip|logs|bypass|pastes|discussions`, `staff.content.view_private` and `staff.content.hide`; - 15 for `admin`, including `staff.users.manage`, `staff.roles.assign`, `staff.site.view|configure` and `staff.streams.end|manage`; - 5 for `owner`: `staff.roles.grant_admin`, `staff.secrets.manage`, `staff.money.freeze|cashouts` and `staff.loyalty.grant`. Each lists the file:line role checks it replaces in Live (210 sites inventoried), Chat, Community, Network and Billing. - **Local powers:** channel and self powers stay with the product and are not staff capabilities. They are listed under `local`. - **Claims** that Network issues: `role`, `is_owner` and `staff_caps`. - **Rules** that a capability alone does not express: - rank and owner protection; - no staff power in service, app or mod tokens, and none through delegation; - vouching only with the moderate capability; - roles change only in Network; - every action is reported to the moderation log. - **`contracts.staff`:** `roles`, `effectiveRole`, `atLeast`, `capabilitiesOf` and `can`. Issued `staff_caps` win over the role. A family grant (`staff.moderation.*`) stays inside one area, and there is no `staff.*` wildcard. - **Adoption comes later:** Network issuing the claims, and Live, Chat and Community replacing their raw role checks. **Capability catalog gaps** (roadmap §30.2), 15 capabilities checked against each service's routes and production: - **Active:** public reads that production serves to anyone: - `live.channel.read`, `live.stream.read` and `live.discovery.read`; - `community.space.read`, `community.thread.read` and `community.pulse.read`; - `search.query.run`: the public index query, suggest and document read. Service tokens keep using `search.query.delegate`. - **Planned:** each names its current routes and the id that guards them today: - `live.owner.resolve`: there is no public route yet; `live.lineage.resolve` stays internal; - `media.upload.create`, `media.derivative.create`, `media.derivative.read`, `media.lifecycle.read` and `media.lifecycle.transition`: Media's guard checks `media.object.upload` / `media.object.read` until it accepts these ids; - `community.space.manage`; - `community.vote.set`: value 0 removes a vote, so there is no `vote.remove`. - **New `publishing` library manifest** (openvibe-publishing v0.2.1, ADR-019), with no `publishing.*` capability. A package has no grant boundary, and the product capabilities (`wiki.*`, `blog.*`, `news.*`) already cover those routes. Network's registry-exposure test needs a `publishing` entry before Network moves to this release. **`mods.mod-manifest@1` 1.0.0 → 1.1.0** (roadmap §28.3 m6). Every 1.0.0 manifest stays valid. The new fields are all optional: - `permissions.readGrants` / `writeGrants` split namespace access into read-only and read-write. - `billingHooks`: `[{ kind: entitlement|checkout, key, description? }]`, never a price. - `dependencies`: `[{ id: mod_…, version: <range>, optional? }]`. Games still validates a local 1.0.0 copy. **Manifests:** the Deals notes now say that Network consumes `deals.watch.matched`. ## 0.33.1 — 2026-09-23 Additive: `compat.js` reports no breaking change against v0.33.0. Follow-ups to the Tools registry, which OpenVibe.Tools now serves in production (`GET /api/v1/tools`, `/api/v1/tools/:id` and `/api/v1/tools/:id/schema` on openvibe.tools and every satellite). **Capabilities and manifests** - `tools.tool.read` is `active` (it was `planned`). The gateway answers for all 169 tools, and the img, audio, docs, text, yt, maps and food satellites answer for their own. Its description now says what the routes answer: CORS for any origin, ETag and 304, `$defs.input: false` for a tool without an API, `400 tools.query.invalid` and `404 tools.tool.not_found`. - `tools.tool.run` and `tools.net.probe` stay `planned` until Tools serves the run API. - Tools 0.4.1: its notes say the registry is served. **`tools.tool@1` 1.0.0 → 1.1.0.** Two optional fields, so every v0.33.0 descriptor stays valid: - `keywords`: the catalogue's search terms (`GET /api/catalog.json` `tools[].keywords`), which `GET /api/v1/tools?q=` matches beside the id, name and summary. Unique, trimmed, non-empty phrases. - `examples`: `[{ title?, input, files?: [{ name?, mime, url? }] }]`, sample runs that the docs and the generated OpenAPI document will publish. Each input is valid against the tool's input schema. The schema refuses examples on a tool without an API. The descriptions now also say: - `output.schema` describes `result.data` whatever the execution. For a job tool, that is the data of the run's result and of its job's result, which are the same object. - `GET /api/v1/tools/:id/schema` has `$defs.input: false` when `api` is false. - There is no `planned` status. A planned catalogue entry is not a tool: it has no descriptor and is not listed. Neither is a mirror (a second build of another tool). Both answer `404 tools.tool.not_found`, with a detail that says the id is planned or names the tool it mirrors. `tools.tool-list@1` says the same, and that `family`, `execution` and `status` take comma lists. A consumer that validates descriptors against v0.33.0 refuses `keywords` and `examples` (`additionalProperties: false`). Tools should start serving them only after it moves its pin to this release, and so should any service that validates descriptors it reads. **`contracts.tools.checkDescriptor`** now checks each example: - its input against the tool's input schema, when the schema is embedded (errors point at `/examples/<i>/input/…`); - its size as JSON against `limits.maxInputBytes`; - its files against `files`: the count within `min` and `max`, and each `mime` allowed by `accept` (`image/*` takes `image/heic`). An input given as `{ $ref }` (the list form) is not fetched, so its examples are checked where the schema is embedded: `GET /api/v1/tools/:id` and the Tools registry test. When there are examples to check, an embedded input schema that does not compile is reported. All 169 of Tools' current descriptors pass, and so do they with their catalogue keywords and their own example inputs added. **Problem codes Tools emits.** They are listed with the routes that answer them, as every service's are (`errors.problem@1` carries any code). - Registry (`tools.tool@1`, `tools.tool-list@1`, `tools.tool.read`): - `400 tools.query.invalid`: an unknown `execution` or `status` value, or an `api` that is not `true` or `false`; - `404 tools.tool.not_found`: an unknown id, and a planned or mirror id, with a detail saying which. - `tools.run-request@1` and `tools.job-request@1`: - `503 tools.unavailable`: a program or upstream the tool needs is missing. A job submission is refused then, rather than failing later. `503 tools.tool.unavailable` is still the run API's refusal for a tool whose descriptor status is `unavailable`. - Coming with the shared guard, not emitted yet: `429 tools.quota.exceeded` (the caller's allowance in the quota class is spent) and `503 tools.busy` (every slot for that kind of work is taken). Both carry `Retry-After`. The run request still lists `quota.exceeded` beside the new code. - Codes a tool chooses, carried as the error of a failed run or job (`tools.run@1`, `tools.job@1`, `tools.job.failed`): `413 tools.pdf.too_many_pages` (over `limits.maxPages`) and `422 tools.pdf.wrong_password`. **Fixtures and tests** - The valid png, dns, jsonminify and port descriptors carry keywords and examples, and the list fixture carries keywords. - New invalid descriptors: an example whose input the tool's schema refuses, an example file that `files.accept` does not allow, examples on a page-only tool, and a blank keyword. - `test/run.js` checks the `tools.tool` and `tools.tool-list` fixtures with `checkDescriptor` and `checkList`, not only the schema, so an invalid fixture can break a rule that JSON Schema cannot express. It also checks: - that a v0.33.0 descriptor, without the new fields, is still valid; - each example rule; - that `tools.tool.read` is `active` while the run capabilities stay `planned`; - that there is no `planned` status; - that each new code is a valid problem code and is listed with its status; - that `tools.quota.exceeded` and `tools.busy` carry `Retry-After`. ## 0.33.0 — 2026-09-23 Additive: `compat.js` reports no breaking change against v0.32.0. **Tools platform API** (new ADR-027). The tools on openvibe.tools become one registry and one run API, open to the SDK, with tiered, weighted quotas. - `tools.tool@1`: the **tool descriptor**, one per catalogue tool. - Identity: `id`, `family`, `name`, `summary`. - `status`: `stable` | `beta` | `preview` | `unavailable`, with `statusReason`. - How it runs: - `execution`: `client` | `sync` | `job`. - `api`: whether the run API exposes the tool. - `run`: `{ method: POST, path: /api/v1/tools/{id}/run, job: { type, operation, preset? } | null, legacy? }`. - Its data: - `input`: a JSON Schema, embedded or `{ $ref }` to `GET /api/v1/tools/:id/schema#/$defs/input`. - `files`: `{ min, max, accept, maxBytes }` or null. - `output`: `{ kind: json|file|files|text, schema?, mime? }`. - `limits`: `timeoutMs`, and optionally `maxDurationSec`, `maxPixels`, `maxPages`, `maxInputBytes`, `perTargetPerMinute`. - Access and cost: - `auth`: `{ anonymous, capability: tools.tool.run | tools.net.probe }`. - `quotaClass` and `cost` (the relative weight quotas count). - `egress`: the server fetches a host the caller chose. - `hosts` and `docs`. The schema enforces these rules: - `api: false` has `run: null`, and `api: true` has a run and an input schema. - Only job tools name a job, and an API job tool always does. - File output through the API comes from a job. - A `tools.net.probe` tool fetches and is never anonymous. - An anonymous egress tool declares `limits.perTargetPerMinute`. - A client tool never fetches. - `unavailable` needs `statusReason`. - JSON output has a schema. - `tools.tool-list@1`: the `GET /api/v1/tools` answer (`tools`, `count`, `updated_at`, optional `families`). - `tools.run-request@1`: the body of `POST /api/v1/tools/{id}/run`, `{ input, files?, wait_ms?, idempotency_key? }`. It can be JSON or multipart, like the jobs API. `files` references a Media object (`{ media_id }`) or a result file of the caller's own job (`{ job_id, index }`), so tools can chain. The refusal codes are listed in the description. - `tools.run@1`: the answer. Either `{ state: succeeded, tool, result: { data?, text?, files? }, took_ms, job? }`, or `{ state: failed|cancelled, tool, error, took_ms, job? }`, or `{ state: queued|running, tool, job, location }` (202). - `tools.job@1`: the jobs API's view of a job, read from `apps/_shared/jobs` `system.view()` today. It carries state, progress, attempts, times, `expires_at`, result files (`storage`, `media`, `url`), error, retry links and references. It also has an optional `tool`, set when the job came from a run. `tools.job-request@1` is the body of `POST /api/v1/jobs`. - `contracts.tools` (`lib/tools.js`) adds three helpers: - `checkDescriptor(d)` applies the schema plus the rules that depend on the id: `run.path` is the tool's own, `$ref`s point at its own schema, `files.min <= files.max`, `legacy` never lists the run API, and the quota class has no tier word. - `checkList(list)` checks every descriptor, that ids and hosts are unique, and that counts are right. - `jobInput(d, input)` returns `{ ...input, ...preset, tool: operation }`. **Capabilities** - These are `planned` until Tools serves their routes: - `tools.tool.read`: public, quota class `tools-read`. `GET /api/v1/tools`, `GET /api/v1/tools/:id` and `GET /api/v1/tools/:id/schema`. - `tools.tool.run`: public, quota class `tools-run`, `tools.run-request@1` → `tools.run@1`. `POST /api/v1/tools/:id/run`. - `tools.net.probe`: **partner**, quota class `tools-probe`. Network grants it only through a staff-set project allowance, never a default one. It covers port, ping, traceroute, mtr, latency and bulk header and TLS checks through the API. - `tools.job.create` adds `POST /api/v1/jobs/:id/retry` and `PUT|DELETE /api/v1/jobs/:id/references/:ref`, and declares `tools.job-request@1` → `tools.job@1`. `tools.job.read` and `tools.job.cancel` declare `tools.job@1`. **Manifests and statuses** - Tools 0.4.0: - lists the three new capabilities; - gains `ready: /api/ready`; - its notes now say that job results are in Media and job events reach OpenVibe.Events in production, that the gateway has no jobs routes yet, and what ADR-027 plans. - `tools.job.created|started|succeeded|failed` are `active` (they were `planned`), because Tools publishes them. Their descriptions no longer say planned. **Tests.** `test/run.js` checks: - every descriptor fixture with `checkDescriptor`; - that a run endpoint exists exactly when `api` is true; - that only job tools name a job; - the egress, probe and client rules, and tier-free quota classes; - that the run capabilities and the descriptor's `auth.capability` agree; - that a run's result files are exactly the job's, and that runs and jobs share the Idempotency-Key rule and the job type pattern; - that the `tools.job.succeeded` event carries a subset of the job view; - the list's counts, ids and hosts. ## 0.32.0 — 2026-09-23 Additive: `compat.js` reports no breaking change against v0.31.0. - **Channel/owner lineage resolver** (roadmap §15.10, requirement D20): one resolver for channel, stream, VOD, clip, Pulse and creator-UI callers. OpenVibe.Live implements it. - `lineage.resolve-request@1`: any of `slug` (a channel, or `<channel>/<slot>`), `parent_slug`, `channel_id`, `stream_id`, `slot_id`, `vod_id`, `clip_id`, `media_object_id` (`med_…` or `legacy:<app>:<kind>:<id>`), `owner_subject` and `legacy_ids` (`live_user_id`, `network_user_id`). `display_name` is accepted and never used. - `lineage.resolution@1`: `status` `resolved` with the canonical `channel` (`id`, `slug`, `owner_subject`, optional `legacy_ids`), whatever of `stream`, `vod`, `clip` and `media_object` the inputs reached, `resolved_by` (the deciding input), `rule`, `confidence` (`exact` | `derived` | `legacy_map`), `via` and `checked`; or `status` `unresolved` with a `reason` (`no_input`, `display_name_only`, `not_found`, `conflict`, `ambiguous`, `source_unavailable`) and no channel. - Precedence, in the roadmap's order: explicit slug, nested or parent slug, channel id, stream lookup (stream, slot), the record's own lineage (clip, VOD), Media lineage, owner subject, legacy maps. The first input that resolves decides, and every other input that resolves must name the same channel or the answer is `conflict`. Inside a record the first link that answers wins: a clip goes to its stream, then its VOD, then the channel recorded on it, never the clipper; a VOD goes to its stream, then its slot, then the owner recorded on it. A display name alone resolves nothing. - New capability `live.lineage.resolve` (Live, internal): `GET|POST /internal/lineage/resolve`. - **Event payload contracts for events already emitted**, each read from the producer's code and checked against payloads it builds: - `tips.interaction.moderated@1` (a paid message filtered, held, hidden or restored, by role; the deliveries a hide cancelled; never the supporter, message, reason or moderator) and `tips.interaction.erased@1` (a supporter erased their data; `redacts` takes back the interaction's earlier events), from OpenVibe.Tips' drafts. New capability `tips.interaction.moderate` (Tips, internal): the moderation queue and log, hide and restore. The Tips manifest (0.4.0) lists all three. - `media.job.proposed|queued|started|retrying|succeeded|failed|cancelled@1`: OpenVibe.Media's job transitions (`server/events.js` `recordJob`), one contract each like `tools.job.*`. The payload is an event projection of the job (`queue.jobEvent`), every field always present: `id`, `app_id`, `object_id`, `type`, `status`, `attempts`, `max_attempts`, `error_code` (the stable code only), `cancel_requested`, `has_result`, and `run_after`, `decided_at`, `created_at`, `updated_at`, `started_at`, `finished_at` as ISO 8601 UTC (`…Z`) or null. Deliberately left out, because events travel beyond the tenant: the tenant's `params`, the caller's `idempotency_key`, the free-text `error`, the `result` (a thumbnail URL, of a private VOD too), `created_by`/`decided_by` and `owner_user_id` (tenant-local user ids). A consumer GETs the job with a tenant token for those (`GET /api/v2/:app/jobs/:id`, unchanged); `has_result` says whether there is a result to fetch. `retrying` carries status `queued`, `started` status `running`; `finished_at` is set exactly on `succeeded`, `failed` and `cancelled`, `started_at` on everything that ran, `run_after` always on `retrying`. No service consumed `media.job.*` before this narrowing. The Media manifest (0.2.0) lists them. - `live.stream.started@1` and `live.stream.ended@1` (public: stream id, channel username, display name, URL and user subject when Live knows it, title, category or null, protocol, NSFW flag, times; `ended` adds `ended_at` and `duration_seconds`) and `live.release.deployed@1` (internal: head commit, short release, previous head, up to 40 new commits, `deployed_at`, `notes_url`). - `network.module.updated@1`: one event per change to a user-module record (write, delete by the person or the owning service, account removal, account merge), with the revision after the change, the changed field names and only the new values of changed public fields. A deleted record carries no values; `merged_into` and `merged_from` mark the two sides of a merge. The Network manifest (0.3.0) produces it and consumes `live.stream.started` (go-live notifications). - `chat.preferences` is owned by `chat` (OpenVibe.Chat since the Wave 6 cutover); the Chat manifest (0.4.0) lists it in `namespacesOwned`. `chat.tts_defaults` stays with Live. `modules.namespace@1`'s description says what account removal and merge do to a person's records, apart from `onOwnerRemoved`. - Routes: `host.site.manage` adds the staff takedowns (`POST|DELETE /api/v1/projects/:id/takedown`, `POST|DELETE /api/v1/sites/:id/takedown`); `wiki.page.create` adds `POST /api/v1/spaces/:space/import` and `wiki.revision.publish` adds `POST /api/v1/pages/:id/revisions/:n/review`. ## 0.31.0 — 2026-09-23 Additive: `compat.js` reports no breaking change against v0.30.2. - `registry.release-manifest` 1.0.0 → 1.1.0 (ADR-016, roadmap Track R). Six optional fields, so a `/release.json` can say what changed and what it still accepts: - `components`: `{ <id>: { kind: style|content|script|server, version } }`. A tab applies a release in place only when every component that changed is `style`, `content` or `server`. `shell` is the catch-all for client code that no other component lists. - `assets`: `{ "<logical path>": { url, component, integrity? } }`, the content-addressed URL of each asset this release serves. - `schema_generation` and `schema_compatible_from` (integers or null): the database schema generation, and the oldest generation whose code still runs against it. A rollback below the second is not data-safe. - `contract_ranges`: `{ <contract id>: { version, accepts: ">=a.b.c <x.y.z", role?: produces|consumes } }`. A page from release A works against a server on release B when A's version is in B's `accepts` and B's version is in A's `accepts`. - `metrics_url`: where tabs POST their update outcome counts (D46). - A consumer that validates manifests against 0.30.x rejects these fields (`additionalProperties: false`). openvibe-shared ≥ 1.5.0 therefore serves only the fields the service's installed openvibe-contracts declares. A service starts serving them by moving its pin to this release. ## 0.30.2 — 2026-09-23 - New capability `live.follower.read` (Live, internal): `GET /internal/followers`, used by OpenVibe.Network's go-live notifications from `live.stream.started`. - `chat.message.send` is owned by `chat` (was `live`) and is also implemented by Chat's service API (`POST /internal/live/calls`); it moves from Live's manifest to Chat's. - Live declares `eventsProduced` (`live.stream.started`, `live.stream.ended`, `live.release.deployed`) and `eventsConsumed` (`openre.session.*`, `media.vod.*`, `media.clip.*`, `media.storage.*`); Media declares its seven `media.*` event types. ## 0.30.1 — 2026-09-23 - `billing.cashout.requested|paid|denied`: `cashout.payout_method` carries the method's `type` only (`additionalProperties: false`). The address (e.g. a PayPal email) stays in Billing and never travels on the retained event stream. No producer had emitted a cashout event yet. ## 0.30.0 (2026-09-23) Additive: `compat.js` reports no breaking change against v0.29.0. No existing contract changed. **Event payload contracts** (ADR-026). Each event type has a contract named after it, with the envelope `version` as its major: ``contracts.validate(`${env.event_type}@${env.version}`, env.payload)``. The files are `contracts/events/payloads/<event_type>.v1.json`, and every one has valid and invalid fixtures. The shapes come from the producers' code. - Chat: `chat.message.deleted` (`message_ids`, and `redacts: { subject_type: chat_message, subject_ids }`, which is required). - Tips: `tips.interaction.ready|failed|cancelled`, `tips.goal.updated`, `tips.overlay.delivered|failed`. - VIP: `vip.plan.published`, `vip.membership.changed`. - Billing: `billing.transaction.settled|reversed`, `billing.entitlement.changed`, `billing.subscription.canceled`, `billing.cashout.requested|paid|denied`, `billing.staff.action`, `billing.receipt.external`. - Wiki: `wiki.space.updated`, `wiki.revision.created`, `wiki.watch.triggered`, `wiki.page.published|updated|unpublished|deleted`, `wiki.index_document.upserted|deleted`. - Blog: `blog.post.created|published|updated|unpublished|deleted`, `blog.schedule.failed`, `blog.index_document.upserted|deleted`. - Sources: `sources.item.created|updated|removed`, `sources.fetch.failed`, `sources.index_document.upserted|deleted`. - Search: `search.document.indexed|removed`. - Deals: `deals.watch.matched`. Trade: `trade.alert.triggered`. - **Planned** (catalog status `planned`, not emitted yet): `ai.run.queued|succeeded|failed|cached` and `tools.job.created|started|succeeded|failed`. Tools job events carry ids, type, owner, state, times, result locations and errors, never inputs, output data, file names or the anonymous session. - `*.index_document.upserted` payloads are `search.index-document@1`, with the owner and type fixed. **Redaction** (new ADR-026, linked from ADR-004): - `events.redaction-directive@1` is `payload.redacts` (`event_ids` and/or `subject_type` + `subject_ids`, up to 1000 each). - `events.tombstone-payload@1` is what a redacted event's payload becomes. - The ADR documents 403 `events.redaction_not_allowed`, 422 `events.invalid_redaction` and the realtime public replay window (`REALTIME_PUBLIC_REPLAY_SECONDS`, default 300, `gap` reason `public_window`). - Delivery signature v2 was already documented (ADR-004, README). **AI** (ADR-015): - `ai.run-request@1` is the body of `POST /api/v1/runs`, and `ai.run@1` is the run resource. - `ai.run.create` now declares both as `inputSchema`/`outputSchema`, and `ai.run.read` declares `outputSchema`. **Manifests** - Service manifests: - chat 0.3.0 produces `chat.message.deleted`. - tools 0.3.0 produces `tools.job.*`. - network 0.2.0 consumes `deals.watch.matched` and `trade.alert.triggered`. - billing 0.3.0 produces `billing.receipt.external`, and tips 0.3.0 consumes it. - ai 0.2.0 lists its planned `ai.run.*`. - Capabilities: `chat.live_bridge.write` lists `chat.message.deleted`, `tools.job.create` lists `tools.job.*` and `ai.run.create` lists `ai.run.*`. - Notes corrected: - Wiki is deployed and public at openvibe.wiki. - Reviews, VIP and Tips are deployed internally and not launched publicly. Tips also loses the stale "Proposed manifest" line. - SDK, Shared and Examples are `alpha`, no longer `placeholder`: SDK and Shared are released libraries, and Examples is a repository with CI. Realtime is the only placeholder left. **Tests** - Every payload contract lives at its event type's path, is listed in its owner's `eventsProduced`, fits in an envelope and rejects a tombstone. - Every consumed event type (wildcards included) has a producing manifest, and each type has one producer. - The directive's limits are checked. - Generated TypeScript declares every type it references.