openvibers/openvibe.contracts Commits

ADRs 009, 013, 014, 016-025: OpenRe boundary, mods, developer projects, client updates, Sources, Search, Publishing, notification delivery, analytics, moderation, client surfaces, themes, marketplace scope

OP OpenVibers committed 23 Sept 2026 · ddedb40fa4 · 14 files, +482 −0


Changes

ddiff --git a/docs/adr/ADR-009-live-openre-boundary.md b/docs/adr/ADR-009-live-openre-boundary.md
nnew file mode 100644
iindex 00000000..aa4f511c
---- /dev/null
++++ b/docs/adr/ADR-009-live-openre-boundary.md
@@@ -0,0 +1,37 @@
++# ADR-009: Live versus OpenRe boundary
++
++**Status:** Accepted 2026-09-23. Gates Wave 7 (OpenRe.Stream).
++
++## Context and current evidence
++
++- Live runs every transport in its own process: RTMP ingest (`node-media-server`, `server/streaming/rtmp-server.js`), WHIP (`whip-handler.js`, werift), the WebRTC SFU (`webrtc-sfu.js`, mediasoup), JSMPEG (`broadcast-server.js`, `jsmpeg-relay.js`), restreaming (`restream-manager.js`) and the Media-backed recorder (`recorder.js`).
++- Restarting Live drops every live RTMP/WHIP/WebRTC session (hazard H1). Deploys wait for idle (`deploy.sh --wait-idle`) as a workaround, not a fix.
++- Stream keys live in `managed_streams.stream_key`; some were exposed before 2026-09-17 and still need rotating.
++
++## Decision
++
++- **OpenRe.Stream owns transport:** stream definitions and keys, ingest sessions (RTMP, WHIP, WebRTC/SFU, JSMPEG), worker generations, restream destinations with health and logs, key rotation (`openre.key.rotate`), recording *requests* to Media, and the segment timeline.
++- **Live owns presentation:** channels, discovery, watch pages, creator UX, chat embedding and moderation surfaces. Live asks OpenRe for sessions and playback descriptors; it never touches an encoder, socket or RTP port.
++- **Media owns recordings:** finalisation, storage and VOD objects stay in Media (ADR-006).
++- Transport runs in worker processes separate from the OpenRe API. An API deploy starts a new worker generation for *new* sessions and lets old workers drain; no deploy of Live or the OpenRe API ends a live transport.
++- Lifecycle is published as events (`openre.session.started|ended`, `openre.output.failed`), not internal POSTs.
++
++## Alternatives considered
++
++- Keep ingest in Live and make Live restarts graceful: rejected; the product process would still own long-lived transports, and every UI deploy would stay risky.
++- One process for API and transport: rejected; the drain guarantee needs the split.
++
++## Migration consequences
++
++OpenRe runs in parallel with Live's ingest and takes one protocol at a time (RTMP → WHIP → JSMPEG → SFU), each behind a per-channel switch. Each switch waits for a maintenance window agreed with the active broadcasters. Every stream key is rotated at the RTMP cutover.
++
++## Rollback
++
++Per protocol: flip the channel switch back to Live's in-process ingest. Live's ingest code stays until the last protocol has run on OpenRe for two weeks without a regression.
++
++## Acceptance tests
++
++- Deploying Live during a broadcast does not interrupt transport or recording.
++- Deploying the OpenRe API does not end worker-owned transports.
++- A failed restream destination never ends the source session.
++- Every stream key that existed before the cutover has been rotated.
ddiff --git a/docs/adr/ADR-013-mod-manifest.md b/docs/adr/ADR-013-mod-manifest.md
nnew file mode 100644
iindex 00000000..9e3fb55c
---- /dev/null
++++ b/docs/adr/ADR-013-mod-manifest.md
@@@ -0,0 +1,34 @@
++# ADR-013: Mod manifest, trust and sandbox/resource model
++
++**Status:** Accepted 2026-09-23. Gates Wave 12 (mods) and Wave 21 Stage C (sandboxed user code).
++
++## Context and current evidence
++
++OpenVibe.Games has an authoritative world with Network SSO but no mod platform. Source.OpenVibe.Games has its own scripting runtime. Nothing in the platform can install, grant, revoke or sandbox third-party code (D39).
++
++## Decision
++
++- **Manifest in Contracts:** a mod is described by `mods/mod-manifest.v1` in OpenVibe.Contracts: id `mod_<ULID>`, version, publisher subject, target runtime (`games.browser`, `games.source`, `live.overlay`, …), requested capabilities, resource budget (CPU ms per tick, memory, storage, outbound hosts), assets as Media object ids, and a compatibility range.
++- **Grants in Network:** installing a mod creates a principal (`mod` subject) whose grants are the *approved subset* of its requested capabilities. A grant can be revoked at once, and revoking it ends the mod's effect on the next tick or request.
++- **Trust tiers are metadata** (`unreviewed`, `reviewed`, `first-party`). A tier changes defaults and discovery, but never the grant check.
++- **Sandbox in Host:** execution, isolation, budgets and metering belong to OpenVibe.Host (Stage C). Until then, mods run only as data plus runtime-provided scripting inside each game's own sandbox.
++- **No simulation sharing:** the browser and Source runtimes keep separate simulation code. Only platform services (identity, Media, Events, grants) are shared.
++
++## Alternatives considered
++
++- A separate mods service: rejected. Its pieces belong to Contracts, Network and Host.
++- Trust tiers as permissions: rejected. A tier label must never bypass a grant.
++
++## Migration consequences
++
++None today: no mods exist. Games adopts the manifest when it adds its first mod hook.
++
++## Rollback
++
++Revoke the grants; the manifest schema stays.
++
++## Acceptance tests
++
++- A capability the mod was denied cannot be reached from its scripted runtime.
++- A revoked mod stops affecting the world.
++- Install, grant, use and revoke all appear in the audit log.
ddiff --git a/docs/adr/ADR-014-developer-projects.md b/docs/adr/ADR-014-developer-projects.md
nnew file mode 100644
iindex 00000000..99545d7e
---- /dev/null
++++ b/docs/adr/ADR-014-developer-projects.md
@@@ -0,0 +1,37 @@
++# ADR-014: Developer project, tenant and quota model
++
++**Status:** Accepted 2026-09-23. Gates Wave 20 (Codes) and Wave 21 (Host tenants).
++
++## Context and current evidence
++
++- Network has OAuth clients (`oauth_clients`) and, since Wave 1, capability grants (`principal_grants`) and an audit (`principal_usage`).
++- Media has per-app tenants with storage quotas; for example, the token-only `community` tenant has 10 GB.
++- No developer-facing project exists. The only external integration, PowerChat, is first-party, and several internal calls still use a shared loopback key.
++
++## Decision
++
++- **Projects are owned by Network.** A project has an owner subject, members with roles, apps, environments (`sandbox`, `production`), credentials, grants and quotas.
++- **Apps are principals.** Each app is an OAuth client with an `app_<ULID>` subject. Its credentials can be inspected, scoped, rotated and revoked. Grants come only from the capability catalog and never exceed the project's allowance.
++- **Quotas are enforced by the owning service.** A quota is expressed per project and capability (for example, Media bytes, Events publish rate, AI tokens). Codes displays quotas; it never enforces them.
++- **Tenancy in other services is keyed by project id.** Media tenants, Host static sites and Events subscriptions all carry `project_id`, so one revocation reaches every service.
++- **Sandbox is separate.** The sandbox environment gets separate credentials and test-flagged money (ADR-012 rule 9). A sandbox token is refused in production.
++- **No internal keys for developers.** No developer-facing path ever accepts `X-Internal-Key`.
++
++## Alternatives considered
++
++- Projects inside Codes: rejected. Codes is a portal; identity and grants must stay in the identity authority.
++
++## Migration consequences
++
++- PowerChat becomes the first project.
++- The first-party services keep their service principals, which are separate from projects.
++
++## Rollback
++
++Projects are additive; disabling the Codes portal leaves grants intact.
++
++## Acceptance tests
++
++- A developer reaches a working Media, Events and capability integration using only public docs, the SDK and scoped credentials.
++- A playground cannot exceed its project's grants.
++- A revoked credential fails everywhere within one token lifetime (5 minutes).
ddiff --git a/docs/adr/ADR-016-active-client-updates.md b/docs/adr/ADR-016-active-client-updates.md
nnew file mode 100644
iindex 00000000..a1902615
---- /dev/null
++++ b/docs/adr/ADR-016-active-client-updates.md
@@@ -0,0 +1,39 @@
++# ADR-016: Active client update safety and the supported mixed-version window
++
++**Status:** Accepted 2026-09-23. Governs Track R.
++
++## Context and current evidence
++
++- Live serves content-hashed asset URLs (`server/web/assets.js`) and posts one rolling deploy notice in chat.
++- `/shared/*` on Network is served from the pinned OpenVibe.Shared release:
++ - plain URLs carry `max-age=300`;
++ - `?v=<hash>` and `/shared/v1/*` URLs are immutable for a year.
++- Cloudflare currently raises short browser TTLs to 4 hours, a zone setting that needs changing to "respect existing headers".
++- Open tabs can run a client built several releases ago against a new server.
++
++## Decision
++
++- **Mixed-version window:** a server supports clients from the current and the previous release of its API contract for at least **24 hours** after a deploy, and never breaks a contract version inside its major (ADR-002).
++- **Assets:** every asset a page loads is either content-hash addressed (immutable) or explicitly short-lived. An HTML document never references a mutable URL for code.
++- **Release manifest:** each web surface serves `/release.json` (service, release id, contract versions, min supported client release). Clients compare on focus/reconnect. When they have fallen outside the window, they show a non-blocking "new version" prompt, and reload automatically only when idle with no unsent input, upload, call or stream.
++- **Protected sessions:** a deploy never ends an upload, a call, a live broadcast or a recording. Host (Wave 21) drains or waits, as Live's `--wait-idle` does today.
++- **Shared chrome:** a release of OpenVibe.Shared must not break a page that pinned the previous minor.
++
++## Alternatives considered
++
++- Forced reload on deploy: rejected; it loses input and interrupts sessions.
++- Service workers for version pinning: deferred; the release manifest is enough and is simpler to reason about.
++
++## Migration consequences
++
++Live, Network, Community, Media and Tools add `/release.json`. The Cloudflare browser-TTL setting is changed so origin cache headers are respected.
++
++## Rollback
++
++The manifest is advisory; removing it returns clients to today's behaviour.
++
++## Acceptance tests
++
++- A client one release behind keeps working for 24 hours after a deploy.
++- A deploy during an upload, call or stream does not interrupt it.
++- An outdated tab prompts and does not reload while the user is typing.
ddiff --git a/docs/adr/ADR-017-sources-home.md b/docs/adr/ADR-017-sources-home.md
nnew file mode 100644
iindex 00000000..c6ac9906
---- /dev/null
++++ b/docs/adr/ADR-017-sources-home.md
@@@ -0,0 +1,38 @@
++# ADR-017: Home of the source registry and ingestion runtime
++
++**Status:** Accepted 2026-09-23. Gates Wave 14.
++
++## Context and current evidence
++
++AI, News, Reviews, Deals, Coupons and Trade all ingest outside content, and none of them owns a source contract (D36). The AI charter says adapters are "registered here". Ingestion, however, is a scheduled, rate-limited, robots-respecting runtime with health and staleness, which does not fit a model-routing service.
++
++## Decision
++
++- **OpenVibe.Sources** (repository created 2026-09-23; port 4720) owns the source registry and the ingestion workers.
++- **The registry** records:
++ - key, name, type, category and endpoints;
++ - the auth mode, by environment variable *name* only;
++ - robots and terms notes, the rate limit and the enabled state;
++ - review and sensitivity flags, and the default indexability.
++- **Every item** carries its source identity, retrieval time, content hash, terms note and parser version.
++- **Every fetch** writes a run with an explicit state. A failed fetch never creates or changes an item.
++- **AI** invokes the adapters Sources defines; it does not host ingestion.
++
++## Alternatives considered
++
++- Adapters inside OpenVibe.AI: rejected, for the reason above.
++- An ingestion copy per product: rejected, because six copies of robots, rate-limit and provenance handling would drift.
++
++## Migration consequences
++
++None of today's code migrates. Live's AI jobs read Live's own data, not outside sources.
++
++## Rollback
++
++Products fall back to no ingestion. Sources never writes into another service.
++
++## Acceptance tests
++
++- A failed source shows as failed, and no synthesized item appears.
++- robots.txt and the rate limit are respected.
++- Every item's provenance is complete.
ddiff --git a/docs/adr/ADR-018-search-home.md b/docs/adr/ADR-018-search-home.md
nnew file mode 100644
iindex 00000000..96e27f27
---- /dev/null
++++ b/docs/adr/ADR-018-search-home.md
@@@ -0,0 +1,35 @@
++# ADR-018: Home of the search index and query contract
++
++**Status:** Accepted 2026-09-23. Gates Wave 14.
++
++## Context and current evidence
++
++There is no index, no index contract and no engine anywhere on the network (D37). Discovery works per product. A permission-aware query spans products, so building it into each product would mean reimplementing it several times.
++
++## Decision
++
++- **OpenVibe.Search** (repository created 2026-09-23; port 4710) owns:
++ - the canonical index document contract (`search/index-document.v1`);
++ - event-fed ingestion with an inbox;
++ - the query API with ACL filtering;
++ - deletion and visibility propagation.
++- **Owners stay authoritative.** Search holds projections keyed by `(owner, type, id, revision)`. An older revision never overwrites a newer one, and a tombstone wins over older upserts.
++- **Engine: measure first.** SQLite FTS5 runs in the service's own database behind an engine interface. PostgreSQL FTS comes when PostgreSQL exists (ADR-007). Meilisearch or OpenSearch are considered only with measured need.
++
++## Alternatives considered
++
++- A contract-only package with a per-product index: rejected, because ACL-correct cross-product search would be rebuilt many times.
++- A module inside Network: rejected. Network is not a dumping ground, and search has its own scale profile.
++
++## Migration consequences
++
++Products publish index documents as they adopt Publishing (Wave 15). Community's forum and pastes are the first candidates.
++
++## Rollback
++
++Search is a projection. It can be dropped and rebuilt from the owners through the reconciliation listing.
++
++## Acceptance tests
++
++- A private or draft document never reaches an unauthorized query, including through counts, facets and snippets.
++- A deletion or visibility change removes the document from results at once.
ddiff --git a/docs/adr/ADR-019-publishing-packages.md b/docs/adr/ADR-019-publishing-packages.md
nnew file mode 100644
iindex 00000000..88f35cd2
---- /dev/null
++++ b/docs/adr/ADR-019-publishing-packages.md
@@@ -0,0 +1,33 @@
++# ADR-019: Home of the shared publishing packages
++
++**Status:** Accepted 2026-09-23. Gates Wave 15.
++
++## Context and current evidence
++
++Seven publication products need drafts, revisions, scheduling, citations, media attachments, discussion references, an SEO/indexability gate, feeds and structured data. The plan forbids a generic `OpenVibe.Content` authority.
++
++## Decision
++
++- **OpenVibe.Publishing** (repository created 2026-09-23) is a **package repository** with no domain, no runtime and no database.
++- Its packages run inside each product and write to that product's own database, using that product's own table prefix.
++- Publication state is always owned by the product.
++- **The indexability gate is deterministic.** It returns explainable `noindex` reasons (thin, unsourced, duplicate, private, draft, unreviewed-sensitive, unreviewed AI output).
++- **Structured data is built only from provided fields.** A missing rating, price, date or author is left out, never defaulted.
++
++## Alternatives considered
++
++- A content service: rejected. It would turn into a second authority for every product's data.
++- Copying the code per product: rejected. Seven copies of the SEO gate would drift.
++
++## Migration consequences
++
++Wiki and Blog (Wave 16) are the first consumers.
++
++## Rollback
++
++A product can pin an older package version or inline the code; no data lives in Publishing.
++
++## Acceptance tests
++
++- Two products share the revision, citation, SEO-gate and feed code without sharing a database or an authority.
++- Generated content defaults to draft and noindex.
ddiff --git a/docs/adr/ADR-020-notification-delivery.md b/docs/adr/ADR-020-notification-delivery.md
nnew file mode 100644
iindex 00000000..1b2d64b4
---- /dev/null
++++ b/docs/adr/ADR-020-notification-delivery.md
@@@ -0,0 +1,40 @@
++# ADR-020: Notification delivery ownership
++
++**Status:** Accepted 2026-09-23. Wave 3 follow-through.
++
++## Context and current evidence
++
++Network is the only notification store (`server/notifications`, with email via Resend).
++
++Producers push directly:
++- Live calls `POST /internal/notifications/push` and `push-bulk`.
++- Since Wave 1, Live holds a service token for these calls, with the internal key kept as a fallback.
++- A failed push is lost.
++
++OpenVibe.Events now provides durable, signed, retried delivery.
++
++## Decision
++
++- **Network keeps authority:** addressing, preferences, digests, read state and the notification store.
++- **Events owns durable transport.** Producers emit *domain* events through their outbox (for example `live.stream.started`, `community.comment.created`). Network subscribes and decides who is notified, using an inbox so a redelivery never notifies twice.
++- **Direct push stays** for transactional messages that have no domain event (sign-in email). Those calls use service tokens only.
++- **No notification text in events.** Producers never put rendered notification text in events; Network renders it.
++
++## Alternatives considered
++
++- Keep direct pushes: rejected, because a Network restart loses notifications.
++- Move notifications into Events: rejected. Events is transport, and user preferences belong with identity.
++
++## Migration consequences
++
++- Go-live notifications move first: Live emits `live.stream.started`, and Network's consumer replaces `golive-notify.js`'s push.
++- The two paths run side by side, deduplicated by event id, until parity is shown; then the push call is removed.
++
++## Rollback
++
++Re-enable the direct push; the consumer's inbox prevents a double notification during the overlap.
++
++## Acceptance tests
++
++- A Network restart during a go-live loses no notification.
++- A redelivered event produces one notification.
ddiff --git a/docs/adr/ADR-021-analytics.md b/docs/adr/ADR-021-analytics.md
nnew file mode 100644
iindex 00000000..438d766a
---- /dev/null
++++ b/docs/adr/ADR-021-analytics.md
@@@ -0,0 +1,33 @@
++# ADR-021: Analytics ownership and privacy bounds
++
++**Status:** Accepted 2026-09-23. Contract now; extraction decision at Wave 22.
++
++## Context and current evidence
++
++Three repositories carry their own `analytics_events`. Aggregation lives in Tools. No retention bound exists anywhere (baseline D07-D10).
++
++## Decision
++
++- **Shared contract, not a shared service.** An `analytics/event.v1` contract in OpenVibe.Contracts carries:
++ - the event name, service, route template (never a raw URL with ids or queries), session id (rotating, not a user id), coarse geo (country), user-agent class and timestamp.
++- **What is never collected:** IP addresses, precise location and subject ids in raw analytics. Subject-level metrics are opt-in product features, not analytics.
++- **Owned rollups stay put.** Each service keeps its own raw events for at most **30 days**. Rollups (daily counts) live with their owner. The cross-site rollup stays in Tools and Network.
++- **Extraction:** whether to create an analytics service is decided at Wave 22, from measured volume.
++
++## Alternatives considered
++
++- An analytics service now: rejected, since nothing is measured yet to size it.
++- Third-party analytics: rejected on privacy grounds.
++
++## Migration consequences
++
++Each service adds a nightly prune (30 days) and maps its existing events to the contract fields.
++
++## Rollback
++
++Pruning is the only destructive step. Export the rollups before enabling it.
++
++## Acceptance tests
++
++- No raw analytics row older than 30 days.
++- No IP address or subject id in any analytics table.
ddiff --git a/docs/adr/ADR-022-moderation-console.md b/docs/adr/ADR-022-moderation-console.md
nnew file mode 100644
iindex 00000000..6db4885d
---- /dev/null
++++ b/docs/adr/ADR-022-moderation-console.md
@@@ -0,0 +1,35 @@
++# ADR-022: Moderation and policy console
++
++**Status:** Accepted 2026-09-23. Revisit at Wave 12.
++
++## Context and current evidence
++
++- Moderation is spread across services:
++ - Live has roles, bans, IP/CIDR bans and chat moderation;
++ - Community has paste and discussion moderation, where services vouch for staff with `X-OV-Staff` and the `community.*.moderate` capabilities;
++ - Network has an admin surface.
++- Staff actions are audited unevenly.
++
++## Decision
++
++- **Network is the staff directory.** It owns staff roles (`admin`, `global_mod`), the moderation capabilities granted to staff tooling, and a **moderation audit log**. Every service reports staff actions to that log as an event: actor, target reference, action, reason.
++- **Enforcement stays with the owner.** Each service enforces moderation on its own entities; Community hides threads, Live bans chatters.
++- **Service vouching:** a service asserts that its user is staff only while it holds the matching moderate capability. The vouch is logged.
++- **No dedicated moderation service.** It is revisited only if moderation becomes a multi-product workflow (queues, appeals) that no single owner can host.
++
++## Alternatives considered
++
++- A moderation service now: rejected, as there are no cross-product workflows yet.
++
++## Migration consequences
++
++Services emit `<service>.moderation.action` events. Network's admin surface lists them.
++
++## Rollback
++
++The events are additive.
++
++## Acceptance tests
++
++- Every staff action in Live, Community and Chat appears in the audit log with actor and reason.
++- A service without the moderate capability cannot vouch for staff.
ddiff --git a/docs/adr/ADR-023-client-surfaces.md b/docs/adr/ADR-023-client-surfaces.md
nnew file mode 100644
iindex 00000000..7f5a3f93
---- /dev/null
++++ b/docs/adr/ADR-023-client-surfaces.md
@@@ -0,0 +1,33 @@
++# ADR-023: First-party client surfaces and their home
++
++**Status:** Accepted 2026-09-23. Needed by Wave 18.
++
++## Context and current evidence
++
++Live carries `browser-extension/` and `hardware/` (Raspberry Pi kiosk companion). Both are clients, not streaming. The coupon helper (Wave 18) needs a home. Source.OpenVibe.Games is a separate simulation runtime.
++
++## Decision
++
++- **OpenVibe.Extensions** will hold first-party client surfaces:
++ - the browser extension (the coupons and merchant helper, built against Coupons' public API with scoped, revocable credentials and no first-party cookie);
++ - the kiosk and hardware companions;
++ - any future desktop client.
++- The repository is created when Wave 18 starts; Live's two directories move there then.
++- **Source.OpenVibe.Games stays its own runtime.** It integrates with identity, Media and Events at the service boundary and shares no simulation code with the browser game.
++
++## Alternatives considered
++
++- Keep the clients in their product repositories: rejected, because client release cadence and store review differ from server deploys.
++
++## Migration consequences
++
++The moves happen at Wave 18, with a redirect README left in Live.
++
++## Rollback
++
++Not applicable. This ADR only decides where the code lives.
++
++## Acceptance tests
++
++- The extension holds no first-party credential.
++- A malicious merchant page cannot read another user's data.
ddiff --git a/docs/adr/ADR-024-theme-authority.md b/docs/adr/ADR-024-theme-authority.md
nnew file mode 100644
iindex 00000000..5abf7ef4
---- /dev/null
++++ b/docs/adr/ADR-024-theme-authority.md
@@@ -0,0 +1,38 @@
++# ADR-024: Theme authority boundary between Network and OpenVibe.Shared
++
++**Status:** Accepted 2026-09-23.
++
++## Context and current evidence
++
++- Network serves the theme catalog and user theme preferences (`server/themes/routes.js`, table `themes`).
++- OpenVibe.Shared ships the built-in theme tokens (`builtin-themes.js`) and the loader that applies a theme before first paint (`theme-loader.js`).
++- Since 2026-09-17 the default theme has been blue (`vibe`).
++
++## Decision
++
++- **Network owns data.** It holds the theme catalog (built-in and user-made), ratings, and each user's preference (as a user module).
++- **Shared owns rendering:**
++ - the token vocabulary (CSS custom properties, including the derived `--on-accent` family);
++ - the built-in theme definitions;
++ - the first-paint loader;
++ - the rule that every site renders a usable page when Network is unreachable (inline critical canvas, deferred loader).
++- **Adding a token** is a Shared minor release. Removing or renaming one is a major release.
++- **No site-local themes.** Products never define their own themes; they consume tokens.
++
++## Alternatives considered
++
++- Themes wholly in Shared: rejected, because user-made themes and preferences are data.
++- Wholly in Network: rejected, because first paint must not depend on a Network request.
++
++## Migration consequences
++
++None. This is how it already works; the ADR records the boundary.
++
++## Rollback
++
++Not applicable.
++
++## Acceptance tests
++
++- With Network down, every site paints with the default theme.
++- A user's preference follows them across sites.
ddiff --git a/docs/adr/ADR-025-marketplace-scope.md b/docs/adr/ADR-025-marketplace-scope.md
nnew file mode 100644
iindex 00000000..b1263652
---- /dev/null
++++ b/docs/adr/ADR-025-marketplace-scope.md
@@@ -0,0 +1,35 @@
++# ADR-025: Marketplace and commerce scope
++
++**Status:** Accepted 2026-09-23. Binding on Wave 19 (Trade).
++
++## Context and current evidence
++
++The design corpus contains a historical branch in which Trade was a marketplace (listings, orders, custody). Nothing of it was built. Money now lives only in Billing (ADR-012), and game items are game state that never bridges to money.
++
++## Decision
++
++- **OpenVibe.Trade is informational.** It covers instruments, watchlists, market observations with source timestamps, alert rules and context.
++- **Excluded:**
++ - no custody;
++ - no order execution;
++ - no escrow;
++ - no listings between users;
++ - no personalised financial advice.
++- **The historical marketplace branch stays open as an unresolved question.** Reopening it needs a new ADR covering the legal basis, Billing flows, fraud and dispute handling, and the ADR-012 classification of anything traded.
++
++## Alternatives considered
++
++- Build a minimal marketplace: rejected, because it would bring money movement, disputes and regulatory exposure with no owner or review.
++
++## Migration consequences
++
++None.
++
++## Rollback
++
++Not applicable.
++
++## Acceptance tests
++
++- No Trade endpoint accepts an order, holds value or gives personalised advice (a route-inventory test in Trade's CI).
++- Stale feeds show as stale, never as invented values.
ddiff --git a/docs/adr/README.md b/docs/adr/README.md
iindex e1cbc1ae..cfc74169 100644
---- a/docs/adr/README.md
++++ b/docs/adr/README.md
@@@ -12,5 +12,20 @@ Platform-wide decisions from the OpenVibe development roadmap (section 10). Each
| [ADR-006](ADR-006-media-objects.md) | Media object, location and namespace model | Accepted 2026-09-22; implementation in progress (OpenVibe.Media Wave 4) |
| [ADR-007](ADR-007-data-ownership.md) | Logical data ownership and database technology | Accepted 2026-09-22 |
| [ADR-008](ADR-008-shared-packages.md) | Shared package publication and release manifests | Accepted 2026-09-22; implementation in progress (OpenVibe.Shared) |
++| [ADR-009](ADR-009-live-openre-boundary.md) | Live versus OpenRe boundary | Accepted 2026-09-23; gates Wave 7 |
| [ADR-011](ADR-011-community-pastes.md) | Community paste and comment ownership migration | Accepted and executed 2026-09-22 |
| [ADR-012](ADR-012-economic-classification.md) | Billing vs loyalty, credits and currency classification | Accepted 2026-09-22; gates Waves 8-10 |
++| [ADR-013](ADR-013-mod-manifest.md) | Mod manifest, trust and sandbox/resource model | Accepted 2026-09-23; gates Waves 12 and 21C |
++| [ADR-014](ADR-014-developer-projects.md) | Developer project, tenant and quota model | Accepted 2026-09-23; gates Waves 20-21 |
++| [ADR-016](ADR-016-active-client-updates.md) | Active client update safety and mixed-version window | Accepted 2026-09-23; Track R |
++| [ADR-017](ADR-017-sources-home.md) | Home of the source registry and ingestion runtime | Accepted 2026-09-23: OpenVibe.Sources |
++| [ADR-018](ADR-018-search-home.md) | Home of the search index and query contract | Accepted 2026-09-23: OpenVibe.Search, FTS first |
++| [ADR-019](ADR-019-publishing-packages.md) | Home of the shared publishing packages | Accepted 2026-09-23: OpenVibe.Publishing, packages only |
++| [ADR-020](ADR-020-notification-delivery.md) | Notification delivery ownership | Accepted 2026-09-23: Network authority, Events transport |
++| [ADR-021](ADR-021-analytics.md) | Analytics ownership and privacy bounds | Accepted 2026-09-23: contract now, 30-day raw retention |
++| [ADR-022](ADR-022-moderation-console.md) | Moderation and policy console | Accepted 2026-09-23: Network module + audit events |
++| [ADR-023](ADR-023-client-surfaces.md) | First-party client surfaces | Accepted 2026-09-23: OpenVibe.Extensions at Wave 18 |
++| [ADR-024](ADR-024-theme-authority.md) | Theme authority between Network and Shared | Accepted 2026-09-23 |
++| [ADR-025](ADR-025-marketplace-scope.md) | Marketplace and commerce scope | Accepted 2026-09-23: Trade stays informational |
++
++ADR-010 (Chat boundary) and ADR-015 (AI workflow and publication ownership) are written with the Wave 6 and Wave 13 extractions.