openvibers/openvibe.network Commits

Track the Wave 0 baseline inputs (services, ownership rules, hazards, D-status, snapshots of names only); the data/ ignore rule had kept them out

OP OpenVibers committed 23 Sept 2026 · 33156ef568 · 7 files, +1847 −0


Changes

ddiff --git a/.gitignore b/.gitignore
iindex 3a572962..d0845985 100644
---- a/.gitignore
++++ b/.gitignore
@@@ -6,3 +6,6 @@ dist/
*.db-wal
*.db-shm
*.log
++
++# Wave 0 baseline inputs are hand-maintained source files, not runtime data.
++!docs/roadmap-baseline/data/
ddiff --git a/docs/roadmap-baseline/data/d-status.json b/docs/roadmap-baseline/data/d-status.json
nnew file mode 100644
iindex 00000000..4e8ee6ab
---- /dev/null
++++ b/docs/roadmap-baseline/data/d-status.json
@@@ -0,0 +1,81 @@
++{
++ "_about": "D01-D46 family status (roadmap 3.2, dispositions per 1.3). generate.js verifies every evidence path exists and evaluates every metric against this baseline's scan. A row whose evidence fails to resolve is reported as UNVERIFIED rather than trusted. Umbrella status only; the atomic ledger (roadmap 25) is later work.",
++ "families": [
++ { "ids": "D01", "name": "Identity/account", "status": "partial", "disposition": "partial", "wave": "W1",
++ "finding": "Network is a real OAuth2/OIDC IdP; Live keeps its own users table and Community translates identity through Live.",
++ "evidence": [ { "repo": "OpenVibe.Network", "path": "server/auth" }, { "repo": "OpenVibe.Community", "path": "server/live-client.js" } ],
++ "metrics": ["tables.OpenVibe.Live.target:OpenVibe.Network"] },
++ { "ids": "D02-D04", "name": "Registry, capabilities, contracts", "status": "absent", "disposition": "missing", "wave": "W1, W2",
++ "finding": "Three partial registries (url_registry, Tools catalog, Sites catalog); contracts are prose in CONTRACTS.md; no capability layer.",
++ "evidence": [ { "repo": "OpenVibe.Network", "path": "server/url-registry.js" }, { "repo": "OpenVibe.Sites", "path": "sites.json" } ],
++ "metrics": ["github.charters", "contract.todo-markers"] },
++ { "ids": "D05-D06", "name": "Policy, staff, user modules", "status": "partial", "disposition": "partial", "wave": "W1, W2",
++ "finding": "Roles and admin exist per service; no shared capability map; versioned user modules do not exist.",
++ "evidence": [ { "repo": "OpenVibe.Network", "path": "server/admin" }, { "repo": "OpenVibe.Live", "path": "server/auth/permissions.js" } ] },
++ { "ids": "D07-D10", "name": "Themes, notifications, social, analytics", "status": "partial", "disposition": "partial", "wave": "W1, W2, Track O",
++ "finding": "Themes and notifications are shared and real; follows exist in both Live and Network; analytics is per site with no retention.",
++ "evidence": [ { "repo": "OpenVibe.Network", "path": "server/themes" }, { "repo": "OpenVibe.Network", "path": "server/notifications" } ],
++ "metrics": ["tables.analytics-copies"] },
++ { "ids": "D11-D13", "name": "Events, realtime, SDK", "status": "absent", "disposition": "missing", "wave": "W2, W3",
++ "finding": "Cross-service effects are direct internal POSTs and HMAC webhooks; every realtime channel is a product-local WebSocket server; no SDK.",
++ "evidence": [ { "repo": "OpenVibe.Media", "path": "server/webhooks.js" } ],
++ "metrics": ["outbound.call-sites", "ws.servers"] },
++ { "ids": "D14-D16", "name": "Media", "status": "partial", "disposition": "partial", "wave": "W4",
++ "finding": "Real service with B2/R2 tiering; predecessor-shaped vods/clips/files tables, still owns paste semantics.",
++ "evidence": [ { "repo": "OpenVibe.Media", "path": "server/vod" }, { "repo": "OpenVibe.Media", "path": "server/pastes" } ],
++ "metrics": ["tables.OpenVibe.Media.target:OpenVibe.Community"] },
++ { "ids": "D17-D20", "name": "Live, OpenRe, channel/VOD lineage", "status": "partial", "disposition": "partial", "wave": "W7",
++ "finding": "Live owns RTMP/WHIP/WebRTC ingest and restream in-process.",
++ "evidence": [ { "repo": "OpenVibe.Live", "path": "server/streaming/rtmp-server.js" }, { "repo": "OpenVibe.Live", "path": "server/streaming/restream-manager.js" } ],
++ "metrics": ["tables.OpenVibe.Live.target:OpenRe.Stream"] },
++ { "ids": "D21-D23", "name": "Chat", "status": "partial", "disposition": "partial", "wave": "W6",
++ "finding": "Rooms, DMs, TTS, soundboard, calls and moderation work, all as a Live module.",
++ "evidence": [ { "repo": "OpenVibe.Live", "path": "server/chat/chat-server.js" }, { "repo": "OpenVibe.Live", "path": "server/streaming/call-server.js" } ],
++ "metrics": ["tables.OpenVibe.Live.target:OpenVibe.Chat"] },
++ { "ids": "D24-D27", "name": "Community", "status": "partial", "disposition": "partial", "wave": "W5",
++ "finding": "SSR paste pages only; no database; comments live in Live, paste semantics in Media and (frozen) Live.",
++ "evidence": [ { "repo": "OpenVibe.Community", "path": "server/pastes" } ],
++ "metrics": ["tables.OpenVibe.Community", "tables.OpenVibe.Live.target:OpenVibe.Community"] },
++ { "ids": "D28-D30", "name": "Billing, Tips, VIP", "status": "partial", "disposition": "partial", "wave": "W8-W10 (ADR-012 first)",
++ "finding": "Money split across Network (OpenCoins wallet) and Live (Vibes/PayPal, subscriptions); no ledger authority or reconciliation.",
++ "evidence": [ { "repo": "OpenVibe.Live", "path": "server/monetization/payments.js" }, { "repo": "OpenVibe.Network", "path": "server/coins" } ],
++ "metrics": ["tables.OpenVibe.Live.target:OpenVibe.Billing", "tables.OpenVibe.Network.target:OpenVibe.Billing"] },
++ { "ids": "D31", "name": "Tools", "status": "built", "disposition": "already-present-and-verified", "wave": "W11",
++ "finding": "Gateway plus 7 satellites running; shared jobs/media primitives absent.",
++ "evidence": [ { "repo": "OpenVibe.Tools", "path": "apps/gateway" } ],
++ "metrics": ["tests.OpenVibe.Tools"] },
++ { "ids": "D32", "name": "Games", "status": "built", "disposition": "already-present-and-verified", "wave": "W12",
++ "finding": "Authoritative world with Network SSO; not on shared chrome, no mod platform.",
++ "evidence": [ { "repo": "OpenVibe.Games", "path": "apps/server/src/net/sso.ts" } ],
++ "metrics": ["tests.OpenVibe.Games"] },
++ { "ids": "D33-D34", "name": "Codes, Host", "status": "absent", "disposition": "missing", "wave": "W20, W21",
++ "finding": "Charter repositories only.", "evidence": [] , "metrics": ["github.charters"] },
++ { "ids": "D35-D38", "name": "AI, content, sources, search", "status": "partial", "disposition": "partial", "wave": "W13-W15",
++ "finding": "Large AI subsystem inside Live; no source registry, search index or publishing runtime.",
++ "evidence": [ { "repo": "OpenVibe.Live", "path": "server/ai/moment-registry.js" }, { "repo": "OpenVibe.Live", "path": "server/ai/llm.js" } ],
++ "metrics": ["tables.OpenVibe.Live.target:OpenVibe.AI"] },
++ { "ids": "D39", "name": "Mods", "status": "absent", "disposition": "missing", "wave": "W12, W21",
++ "finding": "No mod manifest, grants or sandbox in the platform (Source.OpenVibe.Games has its own runtime, not audited here).", "evidence": [] },
++ { "ids": "D40", "name": "Open developer services", "status": "absent", "disposition": "missing", "wave": "W20, W21",
++ "finding": "Only a first-party PowerChat integration; internal calls use shared loopback keys.",
++ "evidence": [ { "repo": "OpenVibe.Live", "path": "server/integrations/powerchat-routes.js" } ],
++ "metrics": ["outbound.internal-key"] },
++ { "ids": "D41", "name": "Independent runtime lifecycle", "status": "partial", "disposition": "partial", "wave": "W7, W21",
++ "finding": "Repos deploy independently and Live is socket-activated, but Live restarts drop RTMP and Media restarts orphan recordings.",
++ "evidence": [ { "repo": "OpenVibe.Live", "path": "deploy" } ], "hazards": ["H1", "H3"] },
++ { "ids": "D42", "name": "Shared component architecture", "status": "partial", "disposition": "partial", "wave": "W2, Track R",
++ "finding": "openvibe-shared is a tagged OpenVibe.Shared release that consumers pin in package.json; a consumer can lag the newest tag or still carry a vendored copy.",
++ "evidence": [ { "repo": "OpenVibe.Shared", "path": "files.js" }, { "repo": "OpenVibe.Network", "path": "package.json" } ],
++ "metrics": ["shared.pins", "shared.drift"] },
++ { "ids": "D43", "name": "Safe active-client updates", "status": "absent", "disposition": "missing", "wave": "Track R",
++ "finding": "Live has a deploy notice and content-hashed assets; no release manifests or update coordinator.",
++ "evidence": [ { "repo": "OpenVibe.Live", "path": "server/chat/deploy-notice.js" }, { "repo": "OpenVibe.Live", "path": "server/web/assets.js" } ] },
++ { "ids": "D44", "name": "Crawlable component rendering", "status": "built", "disposition": "already-present-and-verified", "wave": "Track R (preserve)",
++ "finding": "SSR/static with metadata in Network, Tools, Media, Community and Sites.",
++ "evidence": [ { "repo": "OpenVibe.Community", "path": "server/seo.js" }, { "repo": "OpenVibe.Sites", "path": "build.js" } ] },
++ { "ids": "D45", "name": "Release compatibility and recovery", "status": "absent", "disposition": "missing", "wave": "Track R, W22",
++ "finding": "Deploy is git pull + restart; no versioned releases or mixed-version policy.", "evidence": [], "metrics": ["deploy.drift"] },
++ { "ids": "D46", "name": "Update observability and acceptance", "status": "absent", "disposition": "missing", "wave": "Track R, Track O",
++ "finding": "No CI outside AFResume; no release telemetry.", "evidence": [], "metrics": ["ci.repos"] }
++ ]
++}
ddiff --git a/docs/roadmap-baseline/data/github.json b/docs/roadmap-baseline/data/github.json
nnew file mode 100644
iindex 00000000..7f904b63
---- /dev/null
++++ b/docs/roadmap-baseline/data/github.json
@@@ -0,0 +1,456 @@
++{
++ "owner": "OpenVibers",
++ "collectedAt": "2026-09-22T23:36:12.720Z",
++ "repos": [
++ {
++ "description": "AlexFrison.net",
++ "diskUsage": 23272,
++ "isArchived": false,
++ "name": "AFResume",
++ "pushedAt": "2026-09-21T17:05:17Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": null
++ },
++ {
++ "description": "Simulator.Rest - The most realistic break room simulator to ever exist",
++ "diskUsage": 2517,
++ "isArchived": false,
++ "name": "BreakRoomSimulator",
++ "pushedAt": "2026-09-21T17:05:22Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": null
++ },
++ {
++ "description": "Ingest and restream: stream definitions, keys, sessions, transport workers, outputs and output health.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenRe.Stream",
++ "pushedAt": "2026-09-21T17:05:26Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenRe.Stream",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "Providers, models, routing, prompt templates, workflows, runs, citations, cache and quotas for every product.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.AI",
++ "pushedAt": "2026-09-21T17:05:29Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.AI",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "The isolated money ledger: providers, receipts, subscriptions, entitlements, refunds and payouts.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Billing",
++ "pushedAt": "2026-09-21T17:05:33Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Billing",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "The official OpenVibe blog and a blog for every member: drafts, revisions, series, scheduling, feeds.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Blog",
++ "pushedAt": "2026-09-21T17:05:36Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Blog",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "Rooms, messages, DMs, calls, TTS and audio queues, moderation and presence — one identity, every conversation.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Chat",
++ "pushedAt": "2026-09-21T17:05:40Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Chat",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "The developer portal: registry and contract explorers, credentials, playgrounds, examples, mod/app publishing, governance.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Codes",
++ "pushedAt": "2026-09-21T17:05:43Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Codes",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "",
++ "diskUsage": 689,
++ "isArchived": false,
++ "name": "OpenVibe.Community",
++ "pushedAt": "2026-09-22T23:32:06Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": null
++ },
++ {
++ "description": "Machine-readable contracts for the whole OpenVibe network.",
++ "diskUsage": 89,
++ "isArchived": false,
++ "name": "OpenVibe.Contracts",
++ "pushedAt": "2026-09-22T23:07:34Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Contracts",
++ "stage": "alpha",
++ "code": true,
++ "wave": "W1",
++ "dependsOn": [
++ "OpenVibe.Network"
++ ],
++ "plan": "OpenVibe Development Roadmap (combined), Wave 1; implementation plan rev 3 section 3.1",
++ "updated": "2026-09-22"
++ }
++ },
++ {
++ "description": "Coupon codes with merchant matching, restrictions, expiry and real-people validity reports.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Coupons",
++ "pushedAt": "2026-09-21T17:05:53Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Coupons",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "Deals submitted and voted on by the community, with source, price and freshness always shown.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Deals",
++ "pushedAt": "2026-09-21T17:05:57Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Deals",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "Durable events, subscriptions, delivery, retry, dead letters and replay for the network.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Events",
++ "pushedAt": "2026-09-21T17:06:00Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Events",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "Executable public integration examples, tested in CI against the platform.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Examples",
++ "pushedAt": "2026-09-21T17:06:04Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Examples",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "",
++ "diskUsage": 4675,
++ "isArchived": false,
++ "name": "OpenVibe.Games",
++ "pushedAt": "2026-09-21T17:06:08Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": null
++ },
++ {
++ "description": "The network's deployment/control plane first; then isolated hosting for community sites, bots and mods.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Host",
++ "pushedAt": "2026-09-21T17:06:12Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Host",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "",
++ "diskUsage": 6507,
++ "isArchived": false,
++ "name": "OpenVibe.Live",
++ "pushedAt": "2026-09-22T23:28:24Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": null
++ },
++ {
++ "description": "",
++ "diskUsage": 847,
++ "isArchived": false,
++ "name": "OpenVibe.Media",
++ "pushedAt": "2026-09-22T23:14:07Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": null
++ },
++ {
++ "description": "",
++ "diskUsage": 1306,
++ "isArchived": false,
++ "name": "OpenVibe.Network",
++ "pushedAt": "2026-09-22T23:11:39Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": null
++ },
++ {
++ "description": "Source-backed stories: clustered coverage, cited summaries, perspectives and timelines.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.News",
++ "pushedAt": "2026-09-21T17:06:29Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.News",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "Browser delivery plane for events: authorised WS/SSE topics with cursors, resume and presence.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Realtime",
++ "pushedAt": "2026-09-21T17:06:32Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Realtime",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "Review signals gathered across sources with provenance, entity resolution and honest aggregates.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Reviews",
++ "pushedAt": "2026-09-21T17:06:35Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Reviews",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "Supported browser and server clients for the OpenVibe platform.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.SDK",
++ "pushedAt": "2026-09-21T17:06:39Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.SDK",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "Versioned UI, chrome, SEO, legal and release-client packages every OpenVibe site renders.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Shared",
++ "pushedAt": "2026-09-21T17:06:42Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Shared",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "Static front pages for OpenVibe domains that are not full apps yet",
++ "diskUsage": 271,
++ "isArchived": false,
++ "name": "OpenVibe.Sites",
++ "pushedAt": "2026-09-21T17:06:45Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": null
++ },
++ {
++ "description": "Creator support: tips, goals, paid messages, TTS and media requests, overlays.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Tips",
++ "pushedAt": "2026-09-21T17:06:48Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Tips",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "",
++ "diskUsage": 1416,
++ "isArchived": false,
++ "name": "OpenVibe.Tools",
++ "pushedAt": "2026-09-21T17:06:52Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": null
++ },
++ {
++ "description": "Informational watchlists, sourced market context and alerts. No custody, no order execution.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Trade",
++ "pushedAt": "2026-09-21T17:06:56Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Trade",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "Memberships: plans, perks and benefits recognised across every OpenVibe site.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.VIP",
++ "pushedAt": "2026-09-21T17:06:59Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.VIP",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "Wiki spaces with page trees, revisions, citations, media and discussion — editable together, source-backed.",
++ "diskUsage": 13,
++ "isArchived": false,
++ "name": "OpenVibe.Wiki",
++ "pushedAt": "2026-09-21T17:07:02Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": {
++ "repository": "OpenVibers/OpenVibe.Wiki",
++ "stage": "placeholder",
++ "code": false,
++ "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
++ "updated": "2026-09-21"
++ }
++ },
++ {
++ "description": "",
++ "diskUsage": 200,
++ "isArchived": false,
++ "name": "powerchat-devapp-demo",
++ "pushedAt": "2026-09-21T17:07:06Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": null
++ },
++ {
++ "description": "OpenVibe: Source Game",
++ "diskUsage": 20385,
++ "isArchived": false,
++ "name": "Source.OpenVibe.Games",
++ "pushedAt": "2026-09-21T17:07:12Z",
++ "visibility": "PUBLIC",
++ "defaultBranch": "main",
++ "status": null
++ }
++ ]
++}
ddiff --git a/docs/roadmap-baseline/data/hazards.json b/docs/roadmap-baseline/data/hazards.json
nnew file mode 100644
iindex 00000000..f0e117ca
---- /dev/null
++++ b/docs/roadmap-baseline/data/hazards.json
@@@ -0,0 +1,120 @@
++{
++ "_about": "Wave 0 hazard register (roadmap section 2.6 plus findings from this baseline). 'check' names an automatic evidence check run by generate.js against the repo scan and prod-snapshot.json; hazards without one carry manually verified evidence with a date. Owners are roles until the production-host owner reviews this register (Wave 0 exit criterion).",
++ "reviewedBy": null,
++ "hazards": [
++ {
++ "id": "H1", "title": "Restarting Live drops active RTMP streams",
++ "source": "claude-context deploy-live", "severity": "high",
++ "constraint": "RTMP ingest is owned by the Live Node process; a restart disconnects every RTMP broadcaster.",
++ "owner": "Live maintainers", "wave": "W7 (OpenRe extraction removes the class); W21 encodes the rule in deploy tooling",
++ "mitigation": "Deploy Live with deploy.sh --wait-idle after checking /api/streams; never restart mid-broadcast for UI-only changes (static deploys need no restart).",
++ "status": "open"
++ },
++ {
++ "id": "H2", "title": "Stopping the Live socket unit causes 502s",
++ "source": "claude-context deploy-zero-downtime", "severity": "medium",
++ "constraint": "Live uses systemd socket activation; stopping openvibe-live.socket drops the listening socket.",
++ "owner": "Platform operator", "wave": "W21",
++ "mitigation": "Restart only openvibe-live.service; poll /api/ready (boot ~6s).",
++ "status": "open", "check": "live-socket-unit"
++ },
++ {
++ "id": "H3", "title": "Media restart during a recording orphans finalisation",
++ "source": "claude-context media-vod-offload; roadmap 2.7.6", "severity": "high",
++ "constraint": "Recording/finalisation runs inside the Media process. 55 objects / 14.50 GB already sit under B2 vods-orphans/ (21 Sep 2026).",
++ "owner": "Media maintainers", "wave": "W4 (reconciliation + duration repair) / W7 (recording ownership to OpenRe)",
++ "mitigation": "Do not restart Media while a recording is active; reconcile vods-orphans/ before any retention change.",
++ "status": "open"
++ },
++ {
++ "id": "H4", "title": "managed_streams.stream_key values still need rotation after a past leak",
++ "source": "claude-context seadragon-engineering-pass", "severity": "high",
++ "constraint": "Leaked keys remain valid until rotated; a rotation tool was declined earlier.",
++ "owner": "Live maintainers (security track S)", "wave": "W7 (openre.key.rotate) - or earlier as a standalone rotation",
++ "mitigation": "Ship per-stream key regeneration and rotate every key; notify affected streamers.",
++ "status": "open"
++ },
++ {
++ "id": "H5", "title": "Multilingual Whisper model missing breaks non-English captions",
++ "source": "claude-context japaneseoldguy-i18n", "severity": "medium",
++ "constraint": "Without WHISPER_MODEL_MULTI transcribe.js falls back to the English model.",
++ "owner": "Live maintainers", "wave": "-",
++ "mitigation": "Install the model and set WHISPER_MODEL_MULTI in live.env.",
++ "status": "mitigated", "evidence": "2026-09-22: WHISPER_MODEL_MULTI is set in /etc/openvibe/live.env and the file it names exists on openvibe-oregon (checked with test -f; path not recorded).",
++ "check": "env:live.env:WHISPER_MODEL_MULTI"
++ },
++ {
++ "id": "H6", "title": "YouTube refuses the server IP; the yt tool needs a proxy",
++ "source": "claude-context tools-registry-domains", "severity": "low",
++ "constraint": "Environment problem, not code: yt downloads fail from the host address.",
++ "owner": "Platform operator", "wave": "W11",
++ "mitigation": "Set YT_PROXY and/or YT_COOKIES_FILE for openvibe-tools-yt.",
++ "status": "open", "evidence": "2026-09-22: neither YT_PROXY nor YT_COOKIES_FILE is set in /etc/openvibe/tools.env.",
++ "check": "env-absent:tools.env:YT_PROXY"
++ },
++ {
++ "id": "H7", "title": "Workstation root disk near full; headless Chrome can hit ENOSPC",
++ "source": "claude-context seadragon-engineering-pass", "severity": "low",
++ "constraint": "Browser acceptance runs on the workstation fail when the disk fills.",
++ "owner": "Workstation owner", "wave": "Track Q",
++ "mitigation": "Browser harness must support a remote/containerised runner; prune ~/.claude transcripts (~1.2 GB).",
++ "status": "open", "check": "workstation-disk"
++ },
++ {
++ "id": "H8", "title": "A hardcoded internal secret was previously public",
++ "source": "claude-context", "severity": "high",
++ "constraint": "Any surviving openvibe-internal-* value and the Cloudflare token must be treated as exposed.",
++ "owner": "Platform operator (security track S)", "wave": "W1 (scoped service principals) / Track S",
++ "mitigation": "Rotate INTERNAL_API_KEY per service and the Cloudflare token; move to scoped service tokens.",
++ "status": "open"
++ },
++ {
++ "id": "H9", "title": "Removed features and copy rules must not be undone",
++ "source": "claude-context rs-promo, network-shared-systems, afresume-site, currency-model", "severity": "medium",
++ "constraint": "No RobotStreamer promo campaign, no 'free/$0/no ads' copy, never 'Earn Vibes', never call the old theme 'classic'.",
++ "owner": "Every product wave", "wave": "all",
++ "mitigation": "Product waves check the prohibitions list (roadmap 9.1) before adding copy or campaigns.",
++ "status": "standing-rule"
++ },
++ {
++ "id": "H10", "title": "Commit identity",
++ "source": "claude-context commit-identity", "severity": "medium",
++ "constraint": "Commits must be OpenVibers <[email protected]> with no assistant trailers or co-author lines.",
++ "owner": "Every contributor", "wave": "all",
++ "mitigation": "Per-repo git config user.email = [email protected]; review history before push.",
++ "status": "open", "check": "commit-identity"
++ },
++ {
++ "id": "H11", "title": "analytics_events grows without pruning",
++ "source": "roadmap 2.7.2", "severity": "medium",
++ "constraint": "Raw analytics_events is never pruned although hourly/daily rollups exist; Live analytics.db and network.db are the estate's largest stores.",
++ "owner": "OpenVibe.Shared maintainers (openvibe-shared/analytics.js)", "wave": "Track O / ADR-021 - before any schema wave touching Live or Network",
++ "mitigation": "Add a retention job that deletes raw rows older than the rollup window, then VACUUM in a maintenance window.",
++ "status": "open", "check": "db-size"
++ },
++ {
++ "id": "H12", "title": "No Cloudflare cache-purge step after deploys",
++ "source": "roadmap 2.7.5", "severity": "low",
++ "constraint": "A cacheable static asset stays stale at some edges until max-age expires; the only Cloudflare token is DNS-01 scoped.",
++ "owner": "Platform operator", "wave": "W21 / Track I",
++ "mitigation": "Add a purge-scoped token and a purge step, or short max-age + stale-while-revalidate on non-hashed assets.",
++ "status": "open"
++ },
++ {
++ "id": "H13", "title": "Production drifts from main",
++ "source": "this baseline (prod-snapshot.json)", "severity": "medium",
++ "constraint": "A service running an older commit than main means repo-based reasoning does not describe production.",
++ "owner": "Platform operator", "wave": "W0 (checklist) / W21 (release manifests)",
++ "mitigation": "Deploy or record why each lagging service is held back.",
++ "status": "open", "check": "deploy-drift"
++ },
++ {
++ "id": "H14", "title": "Dead and backup database files on the production host",
++ "source": "this baseline (prod-snapshot.json)", "severity": "low",
++ "constraint": "Zero-table openvibe.db/hobo.db and a 260 MB pre-migration backup sit in /opt/openvibe.live/data; nothing records whether they are needed.",
++ "owner": "Live maintainers", "wave": "W0",
++ "mitigation": "Confirm no code opens them, then delete the empty files and move the backup to /opt/backups with a retention date.",
++ "status": "open", "check": "dead-dbs"
++ }
++ ]
++}
ddiff --git a/docs/roadmap-baseline/data/ownership-rules.json b/docs/roadmap-baseline/data/ownership-rules.json
nnew file mode 100644
iindex 00000000..8ce3686c
---- /dev/null
++++ b/docs/roadmap-baseline/data/ownership-rules.json
@@@ -0,0 +1,304 @@
++{
++ "_about": "Table -> target authority classification (roadmap sections 14.1 and 16). First matching rule wins, per repo. 'keep' means the table stays with its current repo. A table no rule matches is reported as UNCLASSIFIED and fails the Wave 0 exit criterion. Rules with repo 'prod-only' apply to tables found only in production and may name the database file ('db').",
++ "_dispositions": {
++ "keep": "stays with the current owner",
++ "extract": "authority moves to the target service in the named wave; current repo keeps a projection or adapter",
++ "frozen-legacy": "read-only; already superseded, kept for cutover/migration only (never written)",
++ "migrate-then-drop": "data migrates to the target, then the table is removed",
++ "investigate": "no clear owner yet; needs a decision"
++ },
++ "rules": [
++ {
++ "repo": "*",
++ "match": "^analytics_(events|hourly|daily|rate_tracking)$",
++ "target": "keep",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "*",
++ "match": "^(schema_migrations|app_state|site_settings)$",
++ "target": "keep",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Service-local config/bookkeeping."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(vods|clips)$",
++ "target": "OpenVibe.Media",
++ "disposition": "frozen-legacy",
++ "wave": "W4",
++ "note": "Frozen for the Media cutover (AGENTS.md: never write)."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(vod_ai_state|clip_ai_state)$",
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "Live-owned AI state for Media-hosted content."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(pastes|paste_comments|paste_likes)$",
++ "target": "OpenVibe.Community",
++ "disposition": "frozen-legacy",
++ "wave": "W5",
++ "note": "Frozen; Community must own paste semantics."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^comments$",
++ "target": "OpenVibe.Community",
++ "disposition": "extract",
++ "wave": "W5",
++ "note": "Typed reusable comment threads."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(chat_messages|chat_messages_new|chat_timeline_events|chat_ai_summaries|stream_first_chats|dm_\\w+|tts_voice_overrides|emotes|emotes_new|channel_sounds|channel_sounds_new|media_requests|media_request_settings|moderation_actions|channel_moderators|channel_moderation_settings|user_tags|hidden_relay_users|relay_users|pending_ip_messages)$",
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(ai_\\w+|channel_ai_bots|channel_ai_config|stream_memories|stream_recaps|streamer_overviews|translations|easter_egg_solves)$",
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "AI runs, caches and derived artifacts; products keep the published projections."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(managed_streams|restream_destinations|robotstreamer_integrations|robotstreamer_integrations_new|platform_connections|kick_channel_cache|stream_controls|stream_controls_new|control_configs|control_config_buttons|control_whitelist|cameras|camera_presets|camera_profiles)$",
++ "target": "OpenRe.Stream",
++ "disposition": "extract",
++ "wave": "W7",
++ "note": "Stream definitions/keys, outputs, restream and control presets. managed_streams.stream_key rotation is open (hazard H4)."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(transactions|payment_orders)$",
++ "target": "OpenVibe.Billing",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "Real-money Vibes/PayPal records."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^subscriptions$",
++ "target": "OpenVibe.Billing + OpenVibe.VIP",
++ "disposition": "extract",
++ "wave": "W8/W10",
++ "note": "Billing owns the contract/entitlement, VIP the plan/perk UX."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^donation_goals$",
++ "target": "OpenVibe.Tips",
++ "disposition": "extract",
++ "wave": "W9",
++ "note": ""
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(coin_transactions|coin_rewards|coin_redemptions|channel_points|user_cosmetics|user_equipped|user_equipped_tag)$",
++ "target": "OpenVibe.Billing (loyalty ledger)",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "Loyalty/progression, never money. Classification blocks migration."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(users|linked_accounts|verification_keys|user_preferences|user_themes|themes)$",
++ "target": "OpenVibe.Network",
++ "disposition": "extract",
++ "wave": "W1",
++ "note": "Identity truth moves to Network subjects; Live keeps a subject projection + channel state."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^follows$",
++ "target": "OpenVibe.Network",
++ "disposition": "extract",
++ "wave": "W1",
++ "note": "Social graph consolidates in Network."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(api_keys|api_tokens)$",
++ "target": "OpenVibe.Network (principals) / OpenVibe.Codes",
++ "disposition": "extract",
++ "wave": "W1/W20",
++ "note": "hbt_ tokens become scoped principals; raw secrets are never migrated."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(bans|ip_log|anon_ip_mappings|approved_ips|vpn_approvals)$",
++ "target": "OpenVibe.Network (policy)",
++ "disposition": "extract",
++ "wave": "W1",
++ "note": "Platform bans/abuse policy vs product moderation. Admin IP-ban exemption must survive."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(vibe_coding_sessions|vibe_coding_events)$",
++ "target": "OpenVibe.Codes",
++ "disposition": "extract",
++ "wave": "W20",
++ "note": "Scoped vibe-coding publication (roadmap 15.5)."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^game_\\w+$",
++ "target": "OpenVibe.Games",
++ "disposition": "migrate-then-drop",
++ "wave": "W12",
++ "note": "Legacy HoboQuest state (progression, bank, farming, dungeons, quests). /api/game already returns 410."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(powerchat_connections|powerchat_webhook_deliveries)$",
++ "target": "keep",
++ "disposition": "keep",
++ "wave": "W3 (deliveries -> Events)",
++ "note": "External PowerChat integration."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(arena_\\w+|tag_guardian_defeats)$",
++ "target": "keep",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product feature."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "match": "^(channels|streams|stream_analytics|stream_timeline_events|viewer_snapshots|viewer_samples|watch_time|content_views|news_settings)$",
++ "target": "keep",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product data and read models."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "match": "^(wallets|coin_transactions)$",
++ "target": "OpenVibe.Billing (loyalty ledger)",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "OpenCoins are earned loyalty, not money."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "match": "^stream_live_announcements$",
++ "target": "OpenVibe.Events",
++ "disposition": "extract",
++ "wave": "W3",
++ "note": "Go-live fanout rate state; durable delivery moves to Events."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "match": "^(email_delivery_log|push_subscriptions)$",
++ "target": "keep",
++ "disposition": "keep",
++ "wave": "W3 (delivery -> Events outbox)",
++ "note": "ADR-020: Network keeps addressing/authority."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "match": ".*",
++ "target": "keep",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "match": "^(pastes|paste_comments|paste_likes)$",
++ "target": "OpenVibe.Community",
++ "disposition": "frozen-legacy",
++ "wave": "W5 (done 2026-09-22)",
++ "note": "Community is the paste authority since 2026-09-22; Media answers 410 to paste writes (PASTES_FROZEN_APPS=live) and 301s /p/:slug to Community. Screenshot bytes stay here."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "match": ".*",
++ "target": "keep",
++ "disposition": "keep",
++ "wave": "W4 (object model)",
++ "note": "Becomes projections over media_objects/media_locations."
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "match": ".*",
++ "target": "keep",
++ "disposition": "keep",
++ "wave": "W12",
++ "note": "Authoritative game state."
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "match": ".*",
++ "target": "keep",
++ "disposition": "keep",
++ "wave": "-",
++ "note": ""
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "match": ".*",
++ "target": "keep",
++ "disposition": "keep",
++ "wave": "W5",
++ "note": ""
++ },
++ {
++ "repo": "prod-only",
++ "db": "rs-companion.db",
++ "match": ".*",
++ "target": "OpenVibe.Games (import source)",
++ "disposition": "frozen-legacy",
++ "wave": "W12",
++ "note": "RobotStreamer companion game DB (unmodified since Mar 2026); read only by scripts/migrate-rs-companion.js."
++ },
++ {
++ "repo": "prod-only",
++ "db": "live.db",
++ "match": "^canvas_",
++ "target": "OpenVibe.Games",
++ "disposition": "frozen-legacy",
++ "wave": "W12",
++ "note": "Collaborative canvas state (roadmap 14.4); Live code removed in 8fa04cc, data still in live.db."
++ },
++ {
++ "repo": "prod-only",
++ "db": "live.db",
++ "match": "^(arena_\\w+|chatter_\\w+)$",
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "prod-only",
++ "db": "live.db",
++ "match": "^promo_claims$",
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "RobotStreamer promo cashout ledger; campaign removed in a4c8aea and must not be rebuilt. May hold payout records: archive before dropping."
++ },
++ {
++ "repo": "prod-only",
++ "match": ".*",
++ "target": "?",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Exists in a production database but no CREATE TABLE in the repo."
++ }
++ ]
++}
ddiff --git a/docs/roadmap-baseline/data/prod-snapshot.json b/docs/roadmap-baseline/data/prod-snapshot.json
nnew file mode 100644
iindex 00000000..9aed61eb
---- /dev/null
++++ b/docs/roadmap-baseline/data/prod-snapshot.json
@@@ -0,0 +1,808 @@
++{
++ "host": "openvibe-oregon",
++ "collectedAt": "2026-09-22T23:36:01.667Z",
++ "deployments": {
++ "community": {
++ "path": "/opt/openvibe.community",
++ "sha": "d7c6e608d8e27e6ddbfc78a15fa7590036b1dded",
++ "committedAt": "2026-09-22T16:32:05-07:00"
++ },
++ "games": {
++ "path": "/opt/openvibe.games",
++ "sha": "43c11f0fbb49a1d83e90105a95c9616fc209a302",
++ "committedAt": "2026-09-18T04:02:57-07:00"
++ },
++ "live": {
++ "path": "/opt/openvibe.live",
++ "sha": "e26606082958f8cf6bb7c4587c225ca09335b5fe",
++ "committedAt": "2026-09-22T16:28:23-07:00"
++ },
++ "media": {
++ "path": "/opt/openvibe.media",
++ "sha": "ddfefcf47740715f24fe7aa7dea648b4699162fc",
++ "committedAt": "2026-09-22T16:14:04-07:00"
++ },
++ "network": {
++ "path": "/opt/openvibe.network",
++ "sha": "1b79576cb024714e56ae2ccc7728080d3d3e0118",
++ "committedAt": "2026-09-22T16:11:37-07:00"
++ },
++ "sites": {
++ "path": "/opt/openvibe.sites",
++ "sha": "185014ff01ed8bd8f6df3dd5099e2b6c904e235d",
++ "committedAt": "2026-09-21T09:22:26-07:00"
++ },
++ "tools": {
++ "path": "/opt/openvibe.tools",
++ "sha": "0346edc57fb9f588abcdea171a22a5ca33f772d2",
++ "committedAt": "2026-09-21T09:58:34-07:00"
++ }
++ },
++ "units": [
++ {
++ "unit": "openvibe-community.service",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ },
++ {
++ "unit": "openvibe-games.service",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ },
++ {
++ "unit": "openvibe-live.service",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ },
++ {
++ "unit": "openvibe-media.service",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ },
++ {
++ "unit": "openvibe-network.service",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ },
++ {
++ "unit": "openvibe-tools-audio.service",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ },
++ {
++ "unit": "openvibe-tools-docs.service",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ },
++ {
++ "unit": "openvibe-tools-food.service",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ },
++ {
++ "unit": "openvibe-tools-img.service",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ },
++ {
++ "unit": "openvibe-tools-maps.service",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ },
++ {
++ "unit": "openvibe-tools-text.service",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ },
++ {
++ "unit": "openvibe-tools-yt.service",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ },
++ {
++ "unit": "openvibe-tools.service",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ },
++ {
++ "unit": "openvibe-live.socket",
++ "load": "loaded",
++ "active": "active",
++ "sub": "running"
++ }
++ ],
++ "databases": [
++ {
++ "file": "/opt/openvibe.community/data/community.db",
++ "bytes": 114688,
++ "tables": [
++ "import_hold",
++ "legacy_id_map",
++ "migration_runs",
++ "paste_comments",
++ "paste_likes",
++ "paste_versions",
++ "paste_visits",
++ "pastes",
++ "subject_projection"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.games/data/world.db",
++ "bytes": 73728,
++ "tables": [
++ "constraints",
++ "guest_ips",
++ "meta",
++ "players",
++ "world_entities"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.live/data/backups/live-pre-643a2e6.db",
++ "bytes": 264736768,
++ "tables": [
++ "ai_chatbot_configs",
++ "ai_timeline_cache",
++ "ai_usage",
++ "ai_viewer_log",
++ "ai_viewer_threads",
++ "anon_ip_mappings",
++ "api_keys",
++ "api_tokens",
++ "app_state",
++ "approved_ips",
++ "arena_achievements",
++ "arena_battles",
++ "arena_beef_hype",
++ "arena_beef_sides",
++ "arena_beefs",
++ "arena_events",
++ "arena_mic_moments",
++ "arena_profiles",
++ "arena_talk",
++ "arena_talk_hype",
++ "arena_talk_session_hype",
++ "arena_talk_session_topics",
++ "arena_talk_sessions",
++ "arena_talk_topics",
++ "arena_tier_paid",
++ "arena_topic_hype",
++ "arena_topic_members",
++ "arena_topic_moments",
++ "arena_topic_progress",
++ "arena_topic_sides",
++ "arena_topic_threads",
++ "arena_topics",
++ "arena_trash_levels",
++ "arena_viewer_clout",
++ "arena_votes",
++ "arena_xp_log",
++ "bans",
++ "camera_presets",
++ "camera_profiles",
++ "cameras",
++ "canvas_actions",
++ "canvas_bans",
++ "canvas_region_locks",
++ "canvas_settings",
++ "canvas_snapshots",
++ "canvas_tiles",
++ "canvas_user_overrides",
++ "channel_ai_bots",
++ "channel_ai_config",
++ "channel_moderation_settings",
++ "channel_moderators",
++ "channel_points",
++ "channel_sounds",
++ "channels",
++ "chat_ai_summaries",
++ "chat_messages",
++ "chat_timeline_events",
++ "chatter_profiles",
++ "chatter_subjects",
++ "chatter_xp_log",
++ "clip_ai_state",
++ "clips",
++ "coin_redemptions",
++ "coin_rewards",
++ "coin_transactions",
++ "comments",
++ "content_views",
++ "control_config_buttons",
++ "control_configs",
++ "control_whitelist",
++ "dm_blocks",
++ "dm_conversations",
++ "dm_messages",
++ "dm_participants",
++ "donation_goals",
++ "easter_egg_solves",
++ "emotes",
++ "follows",
++ "game_achievements",
++ "game_bank",
++ "game_battle_stats",
++ "game_daily_quest_claims",
++ "game_daily_quest_progress",
++ "game_dungeon_runs",
++ "game_effects",
++ "game_farm_plots",
++ "game_fish_collection",
++ "game_inventory",
++ "game_leaderboard",
++ "game_players",
++ "game_recipes",
++ "game_structures",
++ "game_world_state",
++ "hidden_relay_users",
++ "ip_log",
++ "kick_channel_cache",
++ "linked_accounts",
++ "managed_streams",
++ "media_request_settings",
++ "media_requests",
++ "moderation_actions",
++ "news_settings",
++ "paste_comments",
++ "paste_likes",
++ "pastes",
++ "payment_orders",
++ "pending_ip_messages",
++ "platform_connections",
++ "powerchat_connections",
++ "powerchat_webhook_deliveries",
++ "promo_claims",
++ "relay_users",
++ "restream_destinations",
++ "robotstreamer_integrations",
++ "site_settings",
++ "stream_analytics",
++ "stream_controls",
++ "stream_first_chats",
++ "stream_memories",
++ "stream_recaps",
++ "stream_timeline_events",
++ "streamer_overviews",
++ "streams",
++ "subscriptions",
++ "tag_guardian_defeats",
++ "themes",
++ "transactions",
++ "translations",
++ "tts_voice_overrides",
++ "user_cosmetics",
++ "user_equipped",
++ "user_equipped_tag",
++ "user_preferences",
++ "user_tags",
++ "user_themes",
++ "users",
++ "verification_keys",
++ "vibe_coding_events",
++ "vibe_coding_sessions",
++ "viewer_samples",
++ "viewer_snapshots",
++ "vod_ai_state",
++ "vods",
++ "vpn_approvals",
++ "watch_time"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.live/data/openvibe.db",
++ "bytes": 0,
++ "tables": []
++ },
++ {
++ "file": "/opt/openvibe.live/data/hobo.db",
++ "bytes": 0,
++ "tables": []
++ },
++ {
++ "file": "/opt/openvibe.live/data/analytics.db",
++ "bytes": 1748783104,
++ "tables": [
++ "analytics_daily",
++ "analytics_events",
++ "analytics_hourly",
++ "analytics_rate_tracking"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.live/data/live.db",
++ "bytes": 279158784,
++ "tables": [
++ "ai_chatbot_configs",
++ "ai_timeline_cache",
++ "ai_usage",
++ "ai_viewer_log",
++ "ai_viewer_threads",
++ "anon_ip_mappings",
++ "api_keys",
++ "api_tokens",
++ "app_state",
++ "approved_ips",
++ "arena_achievements",
++ "arena_battles",
++ "arena_beef_hype",
++ "arena_beef_sides",
++ "arena_beefs",
++ "arena_events",
++ "arena_mic_moments",
++ "arena_profiles",
++ "arena_talk",
++ "arena_talk_hype",
++ "arena_talk_session_hype",
++ "arena_talk_session_topics",
++ "arena_talk_sessions",
++ "arena_talk_topics",
++ "arena_tier_paid",
++ "arena_topic_hype",
++ "arena_topic_members",
++ "arena_topic_moments",
++ "arena_topic_progress",
++ "arena_topic_sides",
++ "arena_topic_threads",
++ "arena_topics",
++ "arena_trash_levels",
++ "arena_viewer_clout",
++ "arena_votes",
++ "arena_xp_log",
++ "bans",
++ "camera_presets",
++ "camera_profiles",
++ "cameras",
++ "canvas_actions",
++ "canvas_bans",
++ "canvas_region_locks",
++ "canvas_settings",
++ "canvas_snapshots",
++ "canvas_tiles",
++ "canvas_user_overrides",
++ "channel_ai_bots",
++ "channel_ai_config",
++ "channel_moderation_settings",
++ "channel_moderators",
++ "channel_points",
++ "channel_sounds",
++ "channels",
++ "chat_ai_summaries",
++ "chat_messages",
++ "chat_timeline_events",
++ "chatter_profiles",
++ "chatter_subjects",
++ "chatter_xp_log",
++ "clip_ai_state",
++ "clips",
++ "coin_redemptions",
++ "coin_rewards",
++ "coin_transactions",
++ "comments",
++ "content_views",
++ "control_config_buttons",
++ "control_configs",
++ "control_whitelist",
++ "dm_blocks",
++ "dm_conversations",
++ "dm_messages",
++ "dm_participants",
++ "donation_goals",
++ "easter_egg_solves",
++ "emotes",
++ "follows",
++ "game_achievements",
++ "game_bank",
++ "game_battle_stats",
++ "game_daily_quest_claims",
++ "game_daily_quest_progress",
++ "game_dungeon_runs",
++ "game_effects",
++ "game_farm_plots",
++ "game_fish_collection",
++ "game_inventory",
++ "game_leaderboard",
++ "game_players",
++ "game_recipes",
++ "game_structures",
++ "game_world_state",
++ "hidden_relay_users",
++ "ip_log",
++ "kick_channel_cache",
++ "linked_accounts",
++ "managed_streams",
++ "media_request_settings",
++ "media_requests",
++ "moderation_actions",
++ "news_settings",
++ "paste_comments",
++ "paste_likes",
++ "pastes",
++ "payment_orders",
++ "pending_ip_messages",
++ "platform_connections",
++ "powerchat_connections",
++ "powerchat_webhook_deliveries",
++ "promo_claims",
++ "relay_users",
++ "restream_destinations",
++ "robotstreamer_integrations",
++ "schema_migrations",
++ "site_settings",
++ "stream_analytics",
++ "stream_controls",
++ "stream_first_chats",
++ "stream_memories",
++ "stream_recaps",
++ "stream_timeline_events",
++ "streamer_overviews",
++ "streams",
++ "subscriptions",
++ "tag_guardian_defeats",
++ "themes",
++ "transactions",
++ "translations",
++ "tts_voice_overrides",
++ "user_cosmetics",
++ "user_equipped",
++ "user_equipped_tag",
++ "user_preferences",
++ "user_tags",
++ "user_themes",
++ "users",
++ "verification_keys",
++ "vibe_coding_events",
++ "vibe_coding_sessions",
++ "viewer_samples",
++ "viewer_snapshots",
++ "vod_ai_state",
++ "vods",
++ "vpn_approvals",
++ "watch_time"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.live/data/rs-companion.db",
++ "bytes": 8638464,
++ "tables": [
++ "active_effects",
++ "ai_conversations",
++ "ai_memory",
++ "analytics_snapshots",
++ "bank_items",
++ "battle_stats",
++ "changelog_entries",
++ "chat_log",
++ "custom_rewards",
++ "daily_claims",
++ "divorce_history",
++ "dungeon_stats",
++ "event_log",
++ "farm_plots",
++ "file_checksums",
++ "fish_profile",
++ "gusher_profile",
++ "inventory",
++ "jo_profile",
++ "love_affinity",
++ "love_profile",
++ "love_proposals",
++ "media_requests",
++ "mine_profile",
++ "mod_persuasion",
++ "music_profile",
++ "roast_dossier",
++ "roast_log",
++ "unlocked_recipes",
++ "user_voice_selection",
++ "user_voices",
++ "users",
++ "woodcut_profile"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.media/data/media.db",
++ "bytes": 5595136,
++ "tables": [
++ "apps",
++ "assets",
++ "clips",
++ "content_views",
++ "content_visits",
++ "files",
++ "media_settings",
++ "paste_comments",
++ "paste_likes",
++ "pastes",
++ "vods"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.network/data/network.db",
++ "bytes": 766472192,
++ "tables": [
++ "analytics_daily",
++ "analytics_events",
++ "analytics_hourly",
++ "analytics_rate_tracking",
++ "anon_ip_log",
++ "anon_users",
++ "audit_log",
++ "chrome_cache",
++ "chrome_hits",
++ "coin_transactions",
++ "email_delivery_log",
++ "email_verification_tokens",
++ "follows",
++ "identity_legacy_map",
++ "ip_log",
++ "linked_accounts",
++ "notification_preferences",
++ "notifications",
++ "oauth_clients",
++ "oauth_codes",
++ "oauth_tokens",
++ "password_reset_tokens",
++ "principal_grants",
++ "principal_usage",
++ "push_subscriptions",
++ "site_settings",
++ "stream_live_announcements",
++ "themes",
++ "tool_domains",
++ "url_registry",
++ "user_effects",
++ "user_history",
++ "user_modules",
++ "user_preferences",
++ "user_sessions",
++ "users",
++ "verification_keys",
++ "wallets"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.tools/apps/audio/data/analytics.db",
++ "bytes": 59912192,
++ "tables": [
++ "analytics_daily",
++ "analytics_events",
++ "analytics_hourly",
++ "analytics_rate_tracking"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.tools/apps/img/data/analytics.db",
++ "bytes": 5537792,
++ "tables": [
++ "analytics_daily",
++ "analytics_events",
++ "analytics_hourly",
++ "analytics_rate_tracking"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.tools/apps/docs/data/analytics.db",
++ "bytes": 1536000,
++ "tables": [
++ "analytics_daily",
++ "analytics_events",
++ "analytics_hourly",
++ "analytics_rate_tracking"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.tools/apps/maps/data/analytics.db",
++ "bytes": 532480,
++ "tables": [
++ "analytics_daily",
++ "analytics_events",
++ "analytics_hourly",
++ "analytics_rate_tracking"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.tools/apps/text/data/analytics.db",
++ "bytes": 5865472,
++ "tables": [
++ "analytics_daily",
++ "analytics_events",
++ "analytics_hourly",
++ "analytics_rate_tracking"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.tools/apps/yt/data/analytics.db",
++ "bytes": 901120,
++ "tables": [
++ "analytics_daily",
++ "analytics_events",
++ "analytics_hourly",
++ "analytics_rate_tracking"
++ ]
++ },
++ {
++ "file": "/opt/openvibe.tools/apps/food/data/analytics.db",
++ "bytes": 413696,
++ "tables": [
++ "analytics_daily",
++ "analytics_events",
++ "analytics_hourly",
++ "analytics_rate_tracking"
++ ]
++ }
++ ],
++ "envFiles": {
++ "community.env": [
++ "BASE_URL",
++ "HOST",
++ "NODE_ENV",
++ "OV_LIVE_INTERNAL_URL",
++ "OV_LIVE_URL",
++ "OV_MEDIA_URL",
++ "OV_NETWORK_INTERNAL_URL",
++ "OV_NETWORK_URL",
++ "OV_OAUTH_CLIENT_ID",
++ "OV_OAUTH_CLIENT_SECRET",
++ "OV_OAUTH_REDIRECT_URI",
++ "PASTES_AUTHORITY",
++ "PORT",
++ "TRUST_PROXY"
++ ],
++ "games.env": [
++ "DB_PATH",
++ "EDITOR_KEY",
++ "HOST",
++ "NODE_ENV",
++ "OV_NETWORK_URL",
++ "OV_OAUTH_CLIENT_ID",
++ "OV_OAUTH_CLIENT_SECRET",
++ "OWNER_USERNAME",
++ "PLAY_URL",
++ "PORT",
++ "SELF_URL",
++ "STATIC_DIR"
++ ],
++ "generated-secrets.env": [
++ "EDITOR_KEY",
++ "INTERNAL_API_KEY",
++ "MEDIA_GAMES_API_KEY",
++ "MEDIA_LIVE_API_KEY",
++ "MEDIA_LIVE_WEBHOOK_SECRET",
++ "MEDIA_TOOLS_API_KEY"
++ ],
++ "live.env": [
++ "ADMIN_PASSWORD",
++ "ADMIN_USERNAME",
++ "BASE_URL",
++ "DB_PATH",
++ "EMOTES_PATH",
++ "ESCROW_HOLD_DAYS",
++ "HOST",
++ "INTERNAL_API_KEY",
++ "JSMPEG_AUDIO_PORT",
++ "JSMPEG_VIDEO_PORT",
++ "JWT_SECRET",
++ "MAX_EMOTES_PER_USER",
++ "MAX_EMOTE_SIZE_KB",
++ "MEDIASOUP_ANNOUNCED_IP",
++ "MEDIASOUP_LISTEN_IP",
++ "MEDIASOUP_MAX_PORT",
++ "MEDIASOUP_MIN_PORT",
++ "MEDIA_API_KEY",
++ "MEDIA_APP_ID",
++ "MEDIA_PUBLIC_URL",
++ "MEDIA_URL",
++ "MEDIA_WEBHOOK_SECRET",
++ "MIN_CASHOUT",
++ "NODE_ENV",
++ "OV_COMMUNITY_URL",
++ "OV_LIVE_URL",
++ "OV_NETWORK_INTERNAL_URL",
++ "OV_NETWORK_PUBLIC_KEY",
++ "OV_NETWORK_URL",
++ "OV_OAUTH_CLIENT_ID",
++ "OV_OAUTH_CLIENT_SECRET",
++ "PASTES_AUTHORITY",
++ "PASTES_ON_COMMUNITY",
++ "PAYPAL_CLIENT_ID",
++ "PAYPAL_CLIENT_SECRET",
++ "PAYPAL_MODE",
++ "PORT",
++ "RS_COMPANION_DB_PATH",
++ "RS_PASSTHROUGH_ROBOTS",
++ "RTMP_CHUNK_SIZE",
++ "RTMP_HOST",
++ "RTMP_PORT",
++ "STAR_STREAMER",
++ "TURN_AUTH_SECRET",
++ "TURN_CREDENTIAL",
++ "TURN_URL",
++ "TURN_USERNAME",
++ "UNSAFE_WORDS_PATH",
++ "WEBRTC_PORT",
++ "WHISPER_BIN",
++ "WHISPER_MODEL",
++ "WHISPER_MODEL_LIVE",
++ "WHISPER_MODEL_MULTI",
++ "WHISPER_VAD_MODEL"
++ ],
++ "media.env": [
++ "CLIPS_PATH",
++ "DB_PATH",
++ "FILES_PATH",
++ "HOST",
++ "INTERNAL_API_KEY",
++ "MEDIA_APPS_SEED",
++ "MEDIA_B2_APP_KEY",
++ "MEDIA_B2_BUCKET",
++ "MEDIA_B2_ENDPOINT",
++ "MEDIA_B2_KEY_ID",
++ "MEDIA_B2_REGION",
++ "MEDIA_R2_ACCESS_KEY_ID",
++ "MEDIA_R2_BUCKET",
++ "MEDIA_R2_ENDPOINT",
++ "MEDIA_R2_SECRET_ACCESS_KEY",
++ "NODE_ENV",
++ "OV_NETWORK_INTERNAL_URL",
++ "OV_NETWORK_URL",
++ "OV_OAUTH_CLIENT_ID",
++ "OV_OAUTH_CLIENT_SECRET",
++ "PASTES_FROZEN_APPS",
++ "PASTES_MOVED_TO",
++ "PASTES_PATH",
++ "PORT",
++ "RTP_PORT_MAX",
++ "RTP_PORT_MIN",
++ "THUMBNAILS_PATH",
++ "VOD_PATH"
++ ],
++ "network.env": [
++ "ADMIN_PASSWORD",
++ "ADMIN_USERNAME",
++ "BASE_URL",
++ "DB_PATH",
++ "HOST",
++ "INTERNAL_API_KEY",
++ "JWT_PRIVATE_KEY",
++ "JWT_PUBLIC_KEY",
++ "NODE_ENV",
++ "NOTIFICATIONS_ENABLED",
++ "NOTIFICATION_EMAIL_CRITICAL_ONLY",
++ "NOTIFICATION_MAX_AGE_DAYS",
++ "OV_GAMES_INTERNAL_URL",
++ "OV_GAMES_URL",
++ "OV_LIVE_INTERNAL_URL",
++ "OV_LIVE_URL",
++ "OV_MEDIA_INTERNAL_URL",
++ "OV_MEDIA_URL",
++ "OV_TOOLS_INTERNAL_URL",
++ "OV_TOOLS_URL",
++ "PORT"
++ ],
++ "tools.env": [
++ "HOST",
++ "INTERNAL_API_KEY",
++ "MEDIA_PUBLIC_URL",
++ "MEDIA_URL",
++ "NODE_ENV",
++ "OV_NETWORK_INTERNAL_URL",
++ "OV_NETWORK_URL",
++ "OV_OAUTH_CLIENT_ID",
++ "OV_OAUTH_CLIENT_SECRET"
++ ]
++ }
++}
ddiff --git a/docs/roadmap-baseline/data/services.json b/docs/roadmap-baseline/data/services.json
nnew file mode 100644
iindex 00000000..b06b85c2
---- /dev/null
++++ b/docs/roadmap-baseline/data/services.json
@@@ -0,0 +1,75 @@
++{
++ "_source": "Hand-maintained from ../../../../CONTRACTS.md (workspace root) and the production unit list. generate.js checks units/paths against prod-snapshot.json and flags drift.",
++ "services": [
++ {
++ "id": "network", "repo": "OpenVibe.Network", "stage": "live",
++ "domains": ["openvibe.network", "auth.openvibe.network", "api.openvibe.network", "admin.openvibe.network", "my.openvibe.network", "themes.openvibe.network"],
++ "ports": { "http": 4000 },
++ "runtime": "Node/Express, SQLite (better-sqlite3)",
++ "units": ["openvibe-network.service"],
++ "deployPath": "/opt/openvibe.network", "envFile": "/etc/openvibe/network.env",
++ "databases": ["data/network.db"],
++ "owns": "identity/OAuth2/OIDC/FedCM, anonymous sessions, themes, notifications + push/email, OpenCoins wallet, url registry + domains, chrome, avatars, admin, serves openvibe-shared browser files at /shared/"
++ },
++ {
++ "id": "live", "repo": "OpenVibe.Live", "stage": "live",
++ "domains": ["openvibe.live", "ingest.openvibe.live", "relay.openvibe.live", "turn.openvibe.live"],
++ "ports": { "http": 3000, "rtmp": [1935, 9935], "mediasoup": "11000-11300/udp", "jsmpegRelayTls": "9710-9789" },
++ "runtime": "Node/Express (socket-activated), SQLite, mediasoup/werift/Node-Media-Server/ffmpeg",
++ "units": ["openvibe-live.socket", "openvibe-live.service"],
++ "deployPath": "/opt/openvibe.live", "envFile": "/etc/openvibe/live.env",
++ "databases": ["data/live.db", "data/analytics.db", "data/rs-companion.db"],
++ "owns": "streaming product + (today) ingest, restream, chat, DMs, TTS, calls, Vibes/PayPal, subscriptions, comments, AI, arena, recap"
++ },
++ {
++ "id": "media", "repo": "OpenVibe.Media", "stage": "partial",
++ "domains": ["openvibe.media"],
++ "ports": { "http": 4100, "rtpIngest": "12000-12199/udp (127.0.0.1)" },
++ "runtime": "Node/Express, SQLite, ffmpeg, B2 + R2 (S3 API)",
++ "units": ["openvibe-media.service"],
++ "deployPath": "/opt/openvibe.media", "envFile": "/etc/openvibe/media.env",
++ "databases": ["data/media.db"],
++ "owns": "Media API v1 (:app tenancy), VOD/clip recording + cutting, pastes (bytes + semantics today), files, thumbnails, views, avatars, B2/R2 tiering"
++ },
++ {
++ "id": "tools", "repo": "OpenVibe.Tools", "stage": "live",
++ "domains": ["openvibe.tools", "maps.openvibe.tools", "food.openvibe.tools", "img.openvibe.tools", "yt.openvibe.tools", "audio.openvibe.tools", "text.openvibe.tools", "docs.openvibe.tools", "+ ~250 tool hosts"],
++ "ports": { "gateway": 4001, "maps": 4010, "food": 4011, "img": 4012, "yt": 4013, "audio": 4014, "text": 4015, "docs": 4016 },
++ "runtime": "8 Node/Express apps behind a catalog-driven gateway",
++ "units": ["openvibe-tools.service", "openvibe-tools-maps.service", "openvibe-tools-food.service", "openvibe-tools-img.service", "openvibe-tools-yt.service", "openvibe-tools-audio.service", "openvibe-tools-text.service", "openvibe-tools-docs.service"],
++ "deployPath": "/opt/openvibe.tools", "envFile": "/etc/openvibe/tools.env",
++ "databases": ["apps/<satellite>/data/analytics.db"],
++ "owns": "tool catalog, SSR tool pages, tool algorithms, per-satellite analytics"
++ },
++ {
++ "id": "games", "repo": "OpenVibe.Games", "stage": "live",
++ "domains": ["openvibe.games", "play.openvibe.games"],
++ "ports": { "http": 8000 },
++ "runtime": "TypeScript pnpm workspace, raw http.Server + ws, Babylon.js/Havok, SQLite",
++ "units": ["openvibe-games.service"],
++ "deployPath": "/opt/openvibe.games", "envFile": "/etc/openvibe/games.env",
++ "databases": ["data/world.db"],
++ "owns": "Scraplandia authoritative world, editor, player persistence"
++ },
++ {
++ "id": "community", "repo": "OpenVibe.Community", "stage": "partial",
++ "domains": ["openvibe.community"],
++ "ports": { "http": 4200 },
++ "runtime": "Node/Express SSR, no database of its own",
++ "units": ["openvibe-community.service"],
++ "deployPath": "/opt/openvibe.community", "envFile": "/etc/openvibe/community.env",
++ "databases": [],
++ "owns": "SSR paste pages; proxies /api/pastes/* to Live for identity translation"
++ },
++ {
++ "id": "sites", "repo": "OpenVibe.Sites", "stage": "placeholder-host",
++ "domains": ["openvibe.chat", "openre.stream", "openvibe.tips", "openvibe.vip", "openvibe.codes", "openvibe.host", "openvibe.wiki", "openvibe.blog", "openvibe.news", "openvibe.reviews", "openvibe.deals", "openvibe.coupons", "openvibe.trade"],
++ "ports": {},
++ "runtime": "static build (build.js) served by nginx",
++ "units": [],
++ "deployPath": "/opt/openvibe.sites", "envFile": null,
++ "databases": [],
++ "owns": "truthful placeholder pages + nginx vhosts for not-yet-built products"
++ }
++ ]
++}