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
Changes
nnew file mode 100644@@@ -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.nnew file mode 100644@@@ -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.nnew file mode 100644@@@ -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).nnew file mode 100644@@@ -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.nnew file mode 100644@@@ -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.nnew file mode 100644@@@ -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.nnew file mode 100644@@@ -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.nnew file mode 100644@@@ -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.nnew file mode 100644@@@ -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.nnew file mode 100644@@@ -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.nnew file mode 100644@@@ -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.nnew file mode 100644@@@ -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.nnew file mode 100644@@@ -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.@@@ -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.
new file mode 100644
new file mode 100644
@@ -0,0 +1,37 @@
@@ -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.
new file mode 100644
new file mode 100644
@@ -0,0 +1,34 @@
@@ -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.
new file mode 100644
new file mode 100644
@@ -0,0 +1,37 @@
@@ -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).
new file mode 100644
new file mode 100644
@@ -0,0 +1,39 @@
@@ -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.
new file mode 100644
new file mode 100644
@@ -0,0 +1,38 @@
@@ -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.
new file mode 100644
new file mode 100644
@@ -0,0 +1,35 @@
@@ -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.
new file mode 100644
new file mode 100644
@@ -0,0 +1,33 @@
@@ -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.
new file mode 100644
new file mode 100644
@@ -0,0 +1,40 @@
@@ -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.
new file mode 100644
new file mode 100644
@@ -0,0 +1,33 @@
@@ -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.
new file mode 100644
new file mode 100644
@@ -0,0 +1,35 @@
@@ -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.
new file mode 100644
new file mode 100644
@@ -0,0 +1,33 @@
@@ -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.
new file mode 100644
new file mode 100644
@@ -0,0 +1,38 @@
@@ -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.
new file mode 100644
new file mode 100644
@@ -0,0 +1,35 @@
@@ -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.
@@ -12,5 +12,20 @@ Platform-wide decisions from the OpenVibe development roadmap (section 10). Each
@@ -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-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-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-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-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-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.