openvibers/openvibe.network Commits

Wave 0 baseline: generated estate inventory, ownership map, prod discrepancies, D01-D46 status, hazard register

OP OpenVibers committed 22 Sept 2026 · dd1db789b6 · 18 files, +23283 −1

docs/roadmap-baseline/ holds the nine Wave 0 artifacts from the development roadmap,
generated by scripts/roadmap-baseline/generate.js from the sibling checkouts plus two
read-only snapshots (collect-prod.sh over SSH: deployed SHAs, units, DB table names,
env var names only; collect-github.sh: repo list + charter STATUS.json).

- 249 tables with current and target owner (0 unclassified), including 61 tables that
  exist only in production (rs-companion.db, orphaned arena v1/v2, canvas, promo ledger)
- 926 routes with mount prefixes resolved, 65 cross-service call sites with auth /
  timeout / retry, WebSocket servers and paths, 82 background jobs
- secret and config variable names by repo and env file (no values read)
- 108 production-vs-repo items; D01-D46 families re-verified against evidence each run
- 14 hazards; findings new to this pass: Games prod runs main's tree under a
  pre-rewrite SHA, dead/backup DB files in live/data, 4 divergent vendored
  openvibe-shared files, assistant co-author trailers still on main in 4 repos

--check exits 1 while an exit criterion is open (only EC7, host-owner review, remains).

Changes

ddiff --git a/docs/roadmap-baseline/01-services.md b/docs/roadmap-baseline/01-services.md
nnew file mode 100644
iindex 00000000..65e19e69
---- /dev/null
++++ b/docs/roadmap-baseline/01-services.md
@@@ -0,0 +1,71 @@
++# Services and repositories
++
++> Generated by `scripts/roadmap-baseline/generate.js`. Do not edit by hand; edit `data/*.json` or the extractor and regenerate.
++
++Deliverable 1: runtimes, ports, units, env files and repository state.
++
++## Running services
++
++| Service | Repo | Stage | Ports | Units | Env file | Deployed | Databases |
++|---|---|---|---|---|---|---|---|
++| network | OpenVibe.Network | live | http 4000 | openvibe-network.service | /etc/openvibe/network.env | 71be248 | data/network.db |
++| live | OpenVibe.Live | live | http 3000, rtmp 1935/9935, mediasoup 11000-11300/udp, jsmpegRelayTls 9710-9789 | openvibe-live.socket, openvibe-live.service | /etc/openvibe/live.env | e31e342 | data/live.db, data/analytics.db, data/rs-companion.db |
++| media | OpenVibe.Media | partial | http 4100, rtpIngest 12000-12199/udp (127.0.0.1) | openvibe-media.service | /etc/openvibe/media.env | 6114bc6 | data/media.db |
++| tools | OpenVibe.Tools | live | gateway 4001, maps 4010, food 4011, img 4012, yt 4013, audio 4014, text 4015, docs 4016 | 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 | /etc/openvibe/tools.env | 0346edc | apps/<satellite>/data/analytics.db |
++| games | OpenVibe.Games | live | http 8000 | openvibe-games.service | /etc/openvibe/games.env | 43c11f0 | data/world.db |
++| community | OpenVibe.Community | partial | http 4200 | openvibe-community.service | /etc/openvibe/community.env | de947ea | none |
++| sites | OpenVibe.Sites | placeholder-host | | - | - | 185014f | none |
++
++## Scanned repositories
++
++| Repo | HEAD | origin/main | Source files | LOC | Test files | CI | Routes | Tables | WS servers | Timers/jobs | TODO(contract) |
++|---|---|---|---|---|---|---|---|---|---|---|---|
++| OpenVibe.Live | e31e342 | e31e342 | 164 | 69622 | 55 | no | 532 | 125 | 8 | 48 | 15 |
++| OpenVibe.Network | 71be248 | 71be248 | 77 | 20399 | 17 | no | 185 | 35 | 0 | 11 | 0 |
++| OpenVibe.Media | 6114bc6 | 6114bc6 | 28 | 8702 | 4 | no | 67 | 15 | 0 | 9 | 0 |
++| OpenVibe.Tools | 0346edc | 0346edc | 117 | 20018 | 2 | no | 99 | 4 | 0 | 12 | 0 |
++| OpenVibe.Community | de947ea | de947ea | 11 | 1754 | 8 | no | 22 | 4 | 0 | 0 | 0 |
++| OpenVibe.Games | 5710287 | 5710287 | 101 | 18511 | 56 | no | 21 | 5 | 2 | 2 | 0 |
++| OpenVibe.Sites | 185014f | 185014f | 1 | 306 | 0 | no | 0 | 0 | 0 | 0 | 0 |
++
++LOC counts non-test source files in the scanned directories only (`server/`, `apps/`, `packages/`), not frontend assets.
++
++
++## GitHub account census
++
++| Repository | Visibility | Stage (STATUS.json) | Last push | Size | Description |
++|---|---|---|---|---|---|
++| AFResume | PUBLIC | - | 2026-09-21 | 23272 KB | AlexFrison.net |
++| BreakRoomSimulator | PUBLIC | - | 2026-09-21 | 2517 KB | Simulator.Rest - The most realistic break room simulator to ever exist |
++| OpenRe.Stream | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Ingest and restream: stream definitions, keys, sessions, transport workers, outputs and output health. |
++| OpenVibe.AI | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Providers, models, routing, prompt templates, workflows, runs, citations, cache and quotas for every product. |
++| OpenVibe.Billing | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | The isolated money ledger: providers, receipts, subscriptions, entitlements, refunds and payouts. |
++| OpenVibe.Blog | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | The official OpenVibe blog and a blog for every member: drafts, revisions, series, scheduling, feeds. |
++| OpenVibe.Chat | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Rooms, messages, DMs, calls, TTS and audio queues, moderation and presence — one identity, every conversation. |
++| OpenVibe.Codes | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | The developer portal: registry and contract explorers, credentials, playgrounds, examples, mod/app publishing, governance. |
++| OpenVibe.Community | PUBLIC | - | 2026-09-21 | 687 KB | |
++| OpenVibe.Contracts | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Machine-readable contracts for the whole OpenVibe network. |
++| OpenVibe.Coupons | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Coupon codes with merchant matching, restrictions, expiry and real-people validity reports. |
++| OpenVibe.Deals | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Deals submitted and voted on by the community, with source, price and freshness always shown. |
++| OpenVibe.Events | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Durable events, subscriptions, delivery, retry, dead letters and replay for the network. |
++| OpenVibe.Examples | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Executable public integration examples, tested in CI against the platform. |
++| OpenVibe.Games | PUBLIC | - | 2026-09-21 | 4675 KB | |
++| OpenVibe.Host | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | The network's deployment/control plane first; then isolated hosting for community sites, bots and mods. |
++| OpenVibe.Live | PUBLIC | - | 2026-09-21 | 6455 KB | |
++| OpenVibe.Media | PUBLIC | - | 2026-09-21 | 847 KB | |
++| OpenVibe.Network | PUBLIC | - | 2026-09-21 | 1037 KB | |
++| OpenVibe.News | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Source-backed stories: clustered coverage, cited summaries, perspectives and timelines. |
++| OpenVibe.Realtime | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Browser delivery plane for events: authorised WS/SSE topics with cursors, resume and presence. |
++| OpenVibe.Reviews | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Review signals gathered across sources with provenance, entity resolution and honest aggregates. |
++| OpenVibe.SDK | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Supported browser and server clients for the OpenVibe platform. |
++| OpenVibe.Shared | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Versioned UI, chrome, SEO, legal and release-client packages every OpenVibe site renders. |
++| OpenVibe.Sites | PUBLIC | - | 2026-09-21 | 271 KB | Static front pages for OpenVibe domains that are not full apps yet |
++| OpenVibe.Tips | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Creator support: tips, goals, paid messages, TTS and media requests, overlays. |
++| OpenVibe.Tools | PUBLIC | - | 2026-09-21 | 1416 KB | |
++| OpenVibe.Trade | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Informational watchlists, sourced market context and alerts. No custody, no order execution. |
++| OpenVibe.VIP | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Memberships: plans, perks and benefits recognised across every OpenVibe site. |
++| OpenVibe.Wiki | PUBLIC | placeholder (no code) | 2026-09-21 | 13 KB | Wiki spaces with page trees, revisions, citations, media and discussion — editable together, source-backed. |
++| powerchat-devapp-demo | PUBLIC | - | 2026-09-21 | 200 KB | |
++| Source.OpenVibe.Games | PUBLIC | - | 2026-09-21 | 20385 KB | OpenVibe: Source Game |
++
++Collected 2026-09-22 via `gh repo list OpenVibers`.
ddiff --git a/docs/roadmap-baseline/02-schema.md b/docs/roadmap-baseline/02-schema.md
nnew file mode 100644
iindex 00000000..3b19286b
---- /dev/null
++++ b/docs/roadmap-baseline/02-schema.md
@@@ -0,0 +1,309 @@
++# Database schema inventory
++
++> Generated by `scripts/roadmap-baseline/generate.js`. Do not edit by hand; edit `data/*.json` or the extractor and regenerate.
++
++Deliverable 2: every table declared in code or present in production, with current and target owner. Row data was never read.
++
++## Production database files
++
++| File | Size | Tables |
++|---|---|---|
++| /opt/openvibe.games/data/world.db | 74 KB | 5 |
++| /opt/openvibe.live/data/backups/live-pre-643a2e6.db | 264.7 MB | 145 |
++| /opt/openvibe.live/data/openvibe.db | 0 B | 0 |
++| /opt/openvibe.live/data/hobo.db | 0 B | 0 |
++| /opt/openvibe.live/data/analytics.db | 1.75 GB | 4 |
++| /opt/openvibe.live/data/live.db | 279.2 MB | 146 |
++| /opt/openvibe.live/data/rs-companion.db | 8.6 MB | 33 |
++| /opt/openvibe.media/data/media.db | 5.6 MB | 11 |
++| /opt/openvibe.network/data/network.db | 766.5 MB | 34 |
++| /opt/openvibe.tools/apps/audio/data/analytics.db | 59.9 MB | 4 |
++| /opt/openvibe.tools/apps/img/data/analytics.db | 5.5 MB | 4 |
++| /opt/openvibe.tools/apps/docs/data/analytics.db | 1.5 MB | 4 |
++| /opt/openvibe.tools/apps/maps/data/analytics.db | 532 KB | 4 |
++| /opt/openvibe.tools/apps/text/data/analytics.db | 5.9 MB | 4 |
++| /opt/openvibe.tools/apps/yt/data/analytics.db | 901 KB | 4 |
++| /opt/openvibe.tools/apps/food/data/analytics.db | 414 KB | 4 |
++
++## OpenVibe.Live (186)
++
++| Table | Declared at | In prod | Target owner | Disposition | Wave |
++|---|---|---|---|---|---|
++| active_effects | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| ai_chatbot_configs | server/db/database.js:1803 | live.db | OpenVibe.AI | extract | W13 |
++| ai_conversations | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| ai_memory | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| ai_timeline_cache | server/db/database.js:1296 | live.db | OpenVibe.AI | extract | W13 |
++| ai_usage | server/db/database.js:1254 | live.db | OpenVibe.AI | extract | W13 |
++| ai_viewer_log | server/db/database.js:1887 | live.db | OpenVibe.AI | extract | W13 |
++| ai_viewer_threads | server/db/database.js:1873 | live.db | OpenVibe.AI | extract | W13 |
++| analytics_daily | vendor/openvibe-shared/analytics.js:116 | analytics.db | OpenVibe.Live | keep | Track O / ADR-021 |
++| analytics_events | vendor/openvibe-shared/analytics.js:74 | analytics.db | OpenVibe.Live | keep | Track O / ADR-021 |
++| analytics_hourly | vendor/openvibe-shared/analytics.js:98 | analytics.db | OpenVibe.Live | keep | Track O / ADR-021 |
++| analytics_rate_tracking | vendor/openvibe-shared/analytics.js:137 | analytics.db | OpenVibe.Live | keep | Track O / ADR-021 |
++| analytics_snapshots | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| anon_ip_mappings | server/db/database.js:936 | live.db | OpenVibe.Network (policy) | extract | W1 |
++| api_keys | server/db/schema.sql:391 | live.db | OpenVibe.Network (principals) / OpenVibe.Codes | extract | W1/W20 |
++| api_tokens | server/db/schema.sql:404 | live.db | OpenVibe.Network (principals) / OpenVibe.Codes | extract | W1/W20 |
++| app_state | server/db/database.js:1167 | live.db | OpenVibe.Live | keep | - |
++| approved_ips | server/db/database.js:2075 | live.db | OpenVibe.Network (policy) | extract | W1 |
++| arena_achievements | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_battles | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_beef_hype | server/arena/beef.js:58 | live.db | OpenVibe.Live | keep | - |
++| arena_beef_sides | server/arena/beef.js:68 | live.db | OpenVibe.Live | keep | - |
++| arena_beefs | server/arena/beef.js:34 | live.db | OpenVibe.Live | keep | - |
++| arena_events | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_mic_moments | server/arena/mic.js:54 | live.db | OpenVibe.Live | keep | - |
++| arena_profiles | server/arena/arena-service.js:82 | live.db | OpenVibe.Live | keep | - |
++| arena_talk | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_talk_hype | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_talk_session_hype | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_talk_session_topics | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_talk_sessions | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_talk_topics | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_tier_paid | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_topic_hype | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_topic_members | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_topic_moments | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_topic_progress | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_topic_sides | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_topic_threads | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_topics | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_trash_levels | server/arena/mic.js:31 | live.db | OpenVibe.Live | keep | - |
++| arena_viewer_clout | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_votes | (prod only) | live.db | archive then drop | investigate | W0 |
++| arena_xp_log | server/arena/mic.js:45 | live.db | OpenVibe.Live | keep | - |
++| bank_items | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| bans | server/db/schema.sql:418 | live.db | OpenVibe.Network (policy) | extract | W1 |
++| battle_stats | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| camera_presets | server/db/schema.sql:359 | live.db | OpenRe.Stream | extract | W7 |
++| camera_profiles | server/db/schema.sql:339 | live.db | OpenRe.Stream | extract | W7 |
++| cameras | server/db/schema.sql:133 | live.db | OpenRe.Stream | extract | W7 |
++| canvas_actions | (prod only) | live.db | OpenVibe.Games | frozen-legacy | W12 |
++| canvas_bans | (prod only) | live.db | OpenVibe.Games | frozen-legacy | W12 |
++| canvas_region_locks | (prod only) | live.db | OpenVibe.Games | frozen-legacy | W12 |
++| canvas_settings | (prod only) | live.db | OpenVibe.Games | frozen-legacy | W12 |
++| canvas_snapshots | (prod only) | live.db | OpenVibe.Games | frozen-legacy | W12 |
++| canvas_tiles | (prod only) | live.db | OpenVibe.Games | frozen-legacy | W12 |
++| canvas_user_overrides | (prod only) | live.db | OpenVibe.Games | frozen-legacy | W12 |
++| changelog_entries | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| channel_ai_bots | server/db/database.js:1828 | live.db | OpenVibe.AI | extract | W13 |
++| channel_ai_config | server/db/database.js:1852 | live.db | OpenVibe.AI | extract | W13 |
++| channel_moderation_settings | server/db/database.js:1638, server/db/schema.sql:514 | live.db | OpenVibe.Chat | extract | W6 |
++| channel_moderators | server/db/database.js:1621, server/db/schema.sql:499 | live.db | OpenVibe.Chat | extract | W6 |
++| channel_points | server/db/database.js:760 | live.db | OpenVibe.Billing (loyalty ledger) | extract | W8 (after ADR-012) |
++| channel_sounds | server/db/database.js:1741, server/db/schema.sql:625 | live.db | OpenVibe.Chat | extract | W6 |
++| channel_sounds_new | server/db/database.js:1765 | no | OpenVibe.Chat | extract | W6 |
++| channels | server/db/schema.sql:26 | live.db | OpenVibe.Live | keep | - |
++| chat_ai_summaries | server/db/database.js:1308 | live.db | OpenVibe.Chat | extract | W6 |
++| chat_log | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| chat_messages | server/db/schema.sql:148 | live.db | OpenVibe.Chat | extract | W6 |
++| chat_messages_new | server/db/database.js:415 | no | OpenVibe.Chat | extract | W6 |
++| chat_timeline_events | server/db/database.js:1328 | live.db | OpenVibe.Chat | extract | W6 |
++| chatter_profiles | (prod only) | live.db | archive then drop | investigate | W0 |
++| chatter_subjects | (prod only) | live.db | archive then drop | investigate | W0 |
++| chatter_xp_log | (prod only) | live.db | archive then drop | investigate | W0 |
++| clip_ai_state | server/db/database.js:272 | live.db | OpenVibe.AI | extract | W13 |
++| clips | server/db/schema.sql:307 | live.db | OpenVibe.Media | frozen-legacy | W4 |
++| coin_redemptions | server/db/schema.sql:689 | live.db | OpenVibe.Billing (loyalty ledger) | extract | W8 (after ADR-012) |
++| coin_rewards | server/db/schema.sql:668 | live.db | OpenVibe.Billing (loyalty ledger) | extract | W8 (after ADR-012) |
++| coin_transactions | server/db/schema.sql:651 | live.db | OpenVibe.Billing (loyalty ledger) | extract | W8 (after ADR-012) |
++| comments | server/db/database.js:849, server/db/schema.sql:585 | live.db | OpenVibe.Community | extract | W5 |
++| content_views | server/db/schema.sql:328 | live.db | OpenVibe.Live | keep | - |
++| control_config_buttons | server/db/database.js:2306 | live.db | OpenRe.Stream | extract | W7 |
++| control_configs | server/db/database.js:2292 | live.db | OpenRe.Stream | extract | W7 |
++| control_whitelist | server/db/database.js:2277 | live.db | OpenRe.Stream | extract | W7 |
++| custom_rewards | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| daily_claims | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| divorce_history | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| dm_blocks | server/chat/dm.js:57 | live.db | OpenVibe.Chat | extract | W6 |
++| dm_conversations | server/chat/dm.js:21 | live.db | OpenVibe.Chat | extract | W6 |
++| dm_messages | server/chat/dm.js:42 | live.db | OpenVibe.Chat | extract | W6 |
++| dm_participants | server/chat/dm.js:31 | live.db | OpenVibe.Chat | extract | W6 |
++| donation_goals | server/db/schema.sql:264 | live.db | OpenVibe.Tips | extract | W9 |
++| dungeon_stats | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| easter_egg_solves | server/db/database.js:1340 | live.db | OpenVibe.AI | extract | W13 |
++| emotes | server/db/schema.sql:603 | live.db | OpenVibe.Chat | extract | W6 |
++| emotes_new | server/db/database.js:1709 | no | OpenVibe.Chat | extract | W6 |
++| event_log | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| farm_plots | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| file_checksums | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| fish_profile | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| follows | server/db/schema.sql:221 | live.db | OpenVibe.Network | extract | W1 |
++| game_achievements | server/game/game-engine.js:536 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_bank | server/game/schema.sql:77 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_battle_stats | server/game/schema.sql:142 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_daily_quest_claims | server/game/game-engine.js:557, server/game/schema.sql:203 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_daily_quest_progress | server/game/game-engine.js:546, server/game/schema.sql:192 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_dungeon_runs | server/game/schema.sql:156 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_effects | server/game/schema.sql:128 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_farm_plots | server/game/schema.sql:104 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_fish_collection | server/game/game-engine.js:522, server/game/schema.sql:180 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_inventory | server/game/schema.sql:66 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_leaderboard | server/game/schema.sql:168 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_players | server/game/schema.sql:13 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_recipes | server/game/schema.sql:118 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_structures | server/game/schema.sql:87 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| game_world_state | server/game/schema.sql:7 | live.db | OpenVibe.Games | migrate-then-drop | W12 |
++| gusher_profile | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| hidden_relay_users | server/db/database.js:2112 | live.db | OpenVibe.Chat | extract | W6 |
++| inventory | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| ip_log | server/db/database.js:2051 | live.db | OpenVibe.Network (policy) | extract | W1 |
++| jo_profile | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| kick_channel_cache | server/db/database.js:774 | live.db | OpenRe.Stream | extract | W7 |
++| linked_accounts | server/db/database.js:384 | live.db | OpenVibe.Network | extract | W1 |
++| love_affinity | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| love_profile | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| love_proposals | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| managed_streams | server/db/database.js:2420, server/db/schema.sql:78 | live.db | OpenRe.Stream | extract | W7 |
++| media_request_settings | server/db/database.js:869, server/db/schema.sql:722 | live.db | OpenVibe.Chat | extract | W6 |
++| media_requests | server/db/database.js:883, server/db/schema.sql:739 | live.db, rs-companion.db | OpenVibe.Chat | extract | W6 |
++| mine_profile | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| mod_persuasion | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| moderation_actions | server/db/database.js:1920 | live.db | OpenVibe.Chat | extract | W6 |
++| music_profile | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| news_settings | server/news/news-service.js:47 | live.db | OpenVibe.Live | keep | - |
++| paste_comments | server/db/database.js:2008 | live.db | OpenVibe.Community | frozen-legacy | W5 |
++| paste_likes | server/db/database.js:1976 | live.db | OpenVibe.Community | frozen-legacy | W5 |
++| pastes | server/db/database.js:1939 | live.db | OpenVibe.Community | frozen-legacy | W5 |
++| payment_orders | server/db/database.js:1588 | live.db | OpenVibe.Billing | extract | W8 (after ADR-012) |
++| pending_ip_messages | server/db/database.js:2092 | live.db | OpenVibe.Chat | extract | W6 |
++| platform_connections | server/db/database.js:709 | live.db | OpenRe.Stream | extract | W7 |
++| powerchat_connections | server/db/database.js:731 | live.db | OpenVibe.Live | keep | W3 (deliveries -> Events) |
++| powerchat_webhook_deliveries | server/db/database.js:748 | live.db | OpenVibe.Live | keep | W3 (deliveries -> Events) |
++| promo_claims | (prod only) | live.db | archive then drop | investigate | W0 |
++| relay_users | server/db/database.js:1466 | live.db | OpenVibe.Chat | extract | W6 |
++| restream_destinations | server/db/database.js:691 | live.db | OpenRe.Stream | extract | W7 |
++| roast_dossier | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| roast_log | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| robotstreamer_integrations | server/db/database.js:605, server/db/schema.sql:58 | live.db | OpenRe.Stream | extract | W7 |
++| robotstreamer_integrations_new | server/db/database.js:656 | no | OpenRe.Stream | extract | W7 |
++| schema_migrations | server/db/migrations.js:27 | live.db | OpenVibe.Live | keep | - |
++| site_settings | server/db/database.js:353, server/db/schema.sql:556 | live.db | OpenVibe.Live | keep | - |
++| stream_analytics | server/db/schema.sql:810 | live.db | OpenVibe.Live | keep | - |
++| stream_controls | server/db/schema.sql:372 | live.db | OpenRe.Stream | extract | W7 |
++| stream_controls_new | server/db/database.js:2385 | no | OpenRe.Stream | extract | W7 |
++| stream_first_chats | server/db/database.js:2040 | live.db | OpenVibe.Chat | extract | W6 |
++| stream_memories | server/db/database.js:1181 | live.db | OpenVibe.AI | extract | W13 |
++| stream_recaps | server/recap/recap.js:27 | live.db | OpenVibe.AI | extract | W13 |
++| stream_timeline_events | server/db/database.js:1229 | live.db | OpenVibe.Live | keep | - |
++| streamer_overviews | server/db/database.js:1274 | live.db | OpenVibe.AI | extract | W13 |
++| streams | server/db/schema.sql:103 | live.db | OpenVibe.Live | keep | - |
++| subscriptions | server/db/schema.sql:234 | live.db | OpenVibe.Billing + OpenVibe.VIP | extract | W8/W10 |
++| tag_guardian_defeats | server/game/tags.js:81 | live.db | OpenVibe.Live | keep | - |
++| themes | server/db/schema.sql:446 | live.db | OpenVibe.Network | extract | W1 |
++| transactions | server/db/schema.sql:247 | live.db | OpenVibe.Billing | extract | W8 (after ADR-012) |
++| translations | server/db/database.js:506, server/i18n/translate.js:145 | live.db | OpenVibe.AI | extract | W13 |
++| tts_voice_overrides | server/db/database.js:1352 | live.db | OpenVibe.Chat | extract | W6 |
++| unlocked_recipes | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| user_cosmetics | server/db/schema.sql:774, server/monetization/cosmetics.js:109 | live.db | OpenVibe.Billing (loyalty ledger) | extract | W8 (after ADR-012) |
++| user_equipped | server/db/schema.sql:786, server/monetization/cosmetics.js:118 | live.db | OpenVibe.Billing (loyalty ledger) | extract | W8 (after ADR-012) |
++| user_equipped_tag | server/game/tags.js:76 | live.db | OpenVibe.Billing (loyalty ledger) | extract | W8 (after ADR-012) |
++| user_preferences | server/db/schema.sql:825 | live.db | OpenVibe.Network | extract | W1 |
++| user_tags | server/game/tags.js:67 | live.db | OpenVibe.Chat | extract | W6 |
++| user_themes | server/db/schema.sql:467 | live.db | OpenVibe.Network | extract | W1 |
++| user_voice_selection | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| user_voices | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++| users | server/db/schema.sql:5 | live.db, rs-companion.db | OpenVibe.Network | extract | W1 |
++| verification_keys | server/db/database.js:364, server/db/schema.sql:567 | live.db | OpenVibe.Network | extract | W1 |
++| vibe_coding_events | server/db/database.js:2677, server/db/schema.sql:198 | live.db | OpenVibe.Codes | extract | W20 |
++| vibe_coding_sessions | server/db/database.js:2642, server/db/schema.sql:170 | live.db | OpenVibe.Codes | extract | W20 |
++| viewer_samples | server/db/database.js:3956 | live.db | OpenVibe.Live | keep | - |
++| viewer_snapshots | server/db/schema.sql:799 | live.db | OpenVibe.Live | keep | - |
++| vod_ai_state | server/db/database.js:263 | live.db | OpenVibe.AI | extract | W13 |
++| vods | server/db/schema.sql:280 | live.db | OpenVibe.Media | frozen-legacy | W4 |
++| vpn_approvals | server/db/schema.sql:434 | live.db | OpenVibe.Network (policy) | extract | W1 |
++| watch_time | server/db/schema.sql:706 | live.db | OpenVibe.Live | keep | - |
++| woodcut_profile | (prod only) | rs-companion.db | OpenVibe.Games (import source) | frozen-legacy | W12 |
++
++## OpenVibe.Network (35)
++
++| Table | Declared at | In prod | Target owner | Disposition | Wave |
++|---|---|---|---|---|---|
++| admin_rate_limits | server/auth/admin-limits.js:24 | no | OpenVibe.Network | keep | W1 (module split) |
++| analytics_daily | packages/openvibe-shared/analytics.js:116 | network.db | OpenVibe.Network | keep | Track O / ADR-021 |
++| analytics_events | packages/openvibe-shared/analytics.js:74 | network.db | OpenVibe.Network | keep | Track O / ADR-021 |
++| analytics_hourly | packages/openvibe-shared/analytics.js:98 | network.db | OpenVibe.Network | keep | Track O / ADR-021 |
++| analytics_rate_tracking | packages/openvibe-shared/analytics.js:137 | network.db | OpenVibe.Network | keep | Track O / ADR-021 |
++| anon_ip_log | server/db/database.js:443 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| anon_users | server/db/database.js:340 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| audit_log | server/db/database.js:215 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| chrome_cache | server/chrome/service.js:27 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| chrome_hits | server/chrome/service.js:29 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| coin_transactions | server/db/database.js:407 | network.db | OpenVibe.Billing (loyalty ledger) | extract | W8 (after ADR-012) |
++| email_delivery_log | server/db/database.js:302 | network.db | OpenVibe.Network | keep | W3 (delivery -> Events outbox) |
++| email_verification_tokens | server/db/database.js:506 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| follows | server/db/database.js:386 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| ip_log | server/db/database.js:225 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| linked_accounts | server/db/database.js:204 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| notification_preferences | server/db/database.js:290 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| notifications | server/db/database.js:263 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| oauth_clients | server/db/database.js:134 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| oauth_codes | server/db/database.js:144 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| oauth_tokens | server/db/database.js:158 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| password_reset_tokens | server/db/database.js:321 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| push_subscriptions | server/db/database.js:460 | network.db | OpenVibe.Network | keep | W3 (delivery -> Events outbox) |
++| site_settings | server/db/database.js:238 | network.db | OpenVibe.Network | keep | - |
++| stream_live_announcements | server/internal/routes.js:300 | network.db | OpenVibe.Events | extract | W3 |
++| themes | server/db/database.js:183 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| tool_domains | server/domains/routes.js:44 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| url_registry | server/db/database.js:245, server/url-registry.js:7 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| user_effects | server/db/database.js:375 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| user_history | server/history/routes.js:26 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| user_preferences | server/db/database.js:172 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| user_sessions | server/db/database.js:354 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| users | server/db/database.js:113 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| verification_keys | server/db/database.js:422 | network.db | OpenVibe.Network | keep | W1 (module split) |
++| wallets | server/db/database.js:400 | network.db | OpenVibe.Billing (loyalty ledger) | extract | W8 (after ADR-012) |
++
++## OpenVibe.Media (15)
++
++| Table | Declared at | In prod | Target owner | Disposition | Wave |
++|---|---|---|---|---|---|
++| analytics_daily | vendor/openvibe-shared/analytics.js:116 | no | OpenVibe.Media | keep | Track O / ADR-021 |
++| analytics_events | vendor/openvibe-shared/analytics.js:74 | no | OpenVibe.Media | keep | Track O / ADR-021 |
++| analytics_hourly | vendor/openvibe-shared/analytics.js:98 | no | OpenVibe.Media | keep | Track O / ADR-021 |
++| analytics_rate_tracking | vendor/openvibe-shared/analytics.js:137 | no | OpenVibe.Media | keep | Track O / ADR-021 |
++| apps | server/db/schema.sql:8 | media.db | OpenVibe.Media | keep | W4 (object model) |
++| assets | server/db/schema.sql:189 | media.db | OpenVibe.Media | keep | W4 (object model) |
++| clips | server/db/schema.sql:63 | media.db | OpenVibe.Media | keep | W4 (object model) |
++| content_views | server/db/schema.sql:94 | media.db | OpenVibe.Media | keep | W4 (object model) |
++| content_visits | server/views/service.js:43 | media.db | OpenVibe.Media | keep | W4 (object model) |
++| files | server/db/schema.sql:164 | media.db | OpenVibe.Media | keep | W4 (object model) |
++| media_settings | server/db/schema.sql:178 | media.db | OpenVibe.Media | keep | W4 (object model) |
++| paste_comments | server/db/schema.sql:146 | media.db | OpenVibe.Community | extract | W5 |
++| paste_likes | server/db/schema.sql:138 | media.db | OpenVibe.Community | extract | W5 |
++| pastes | server/db/schema.sql:105 | media.db | OpenVibe.Community | extract | W5 |
++| vods | server/db/schema.sql:22 | media.db | OpenVibe.Media | keep | W4 (object model) |
++
++## OpenVibe.Tools (4)
++
++| Table | Declared at | In prod | Target owner | Disposition | Wave |
++|---|---|---|---|---|---|
++| analytics_daily | vendor/openvibe-shared/analytics.js:116 | analytics.db | OpenVibe.Tools | keep | Track O / ADR-021 |
++| analytics_events | vendor/openvibe-shared/analytics.js:74 | analytics.db | OpenVibe.Tools | keep | Track O / ADR-021 |
++| analytics_hourly | vendor/openvibe-shared/analytics.js:98 | analytics.db | OpenVibe.Tools | keep | Track O / ADR-021 |
++| analytics_rate_tracking | vendor/openvibe-shared/analytics.js:137 | analytics.db | OpenVibe.Tools | keep | Track O / ADR-021 |
++
++## OpenVibe.Community (4)
++
++| Table | Declared at | In prod | Target owner | Disposition | Wave |
++|---|---|---|---|---|---|
++| analytics_daily | vendor/openvibe-shared/analytics.js:116 | no | OpenVibe.Community | keep | Track O / ADR-021 |
++| analytics_events | vendor/openvibe-shared/analytics.js:74 | no | OpenVibe.Community | keep | Track O / ADR-021 |
++| analytics_hourly | vendor/openvibe-shared/analytics.js:98 | no | OpenVibe.Community | keep | Track O / ADR-021 |
++| analytics_rate_tracking | vendor/openvibe-shared/analytics.js:137 | no | OpenVibe.Community | keep | Track O / ADR-021 |
++
++## OpenVibe.Games (5)
++
++| Table | Declared at | In prod | Target owner | Disposition | Wave |
++|---|---|---|---|---|---|
++| constraints | packages/persistence/src/sqlite/sqliteStore.ts:55, packages/persistence/src/sqlite/sqliteStore.ts:78 | world.db | OpenVibe.Games | keep | W12 |
++| guest_ips | packages/persistence/src/sqlite/sqliteStore.ts:63, packages/persistence/src/sqlite/sqliteStore.ts:123 | world.db | OpenVibe.Games | keep | W12 |
++| meta | packages/persistence/src/sqlite/sqliteStore.ts:67 | world.db | OpenVibe.Games | keep | W12 |
++| players | packages/persistence/src/sqlite/sqliteStore.ts:36, packages/persistence/src/sqlite/sqliteStore.ts:101 | world.db | OpenVibe.Games | keep | W12 |
++| world_entities | packages/persistence/src/sqlite/sqliteStore.ts:25 | world.db | OpenVibe.Games | keep | W12 |
++
++## OpenVibe.Sites
++
++No tables.
ddiff --git a/docs/roadmap-baseline/03-routes.md b/docs/roadmap-baseline/03-routes.md
nnew file mode 100644
iindex 00000000..8f42f580
---- /dev/null
++++ b/docs/roadmap-baseline/03-routes.md
@@@ -0,0 +1,973 @@
++# HTTP route inventory
++
++> Generated by `scripts/roadmap-baseline/generate.js`. Do not edit by hand; edit `data/*.json` or the extractor and regenerate.
++
++Deliverable 3 (routes half). Paths include the prefix of the `app.use()` mount that loads the router file; Games is matched from its raw `http.Server` handler. Static extraction: a route built at runtime is missed, and a route string in a comment is included.
++
++## Summary
++
++| Repo | Routes | GET | POST | PUT/PATCH | DELETE | Other | /internal/* |
++|---|---|---|---|---|---|---|---|
++| OpenVibe.Live | 532 | 274 | 160 | 50 | 47 | 1 | 6 |
++| OpenVibe.Network | 185 | 80 | 69 | 22 | 14 | 0 | 25 |
++| OpenVibe.Media | 67 | 28 | 27 | 4 | 8 | 0 | 1 |
++| OpenVibe.Tools | 99 | 84 | 12 | 0 | 2 | 1 | 0 |
++| OpenVibe.Community | 22 | 19 | 3 | 0 | 0 | 0 | 0 |
++| OpenVibe.Games | 21 | 0 | 3 | 0 | 0 | 18 | 0 |
++
++
++## OpenVibe.Live
++
++| Method | Path | Source |
++|---|---|---|
++| GET | /api/admin/admins | server/admin/routes.js:762 |
++| POST | /api/admin/admins | server/admin/routes.js:772 |
++| DELETE | /api/admin/admins/:id | server/admin/routes.js:791 |
++| GET | /api/admin/ai/explorer/:userId | server/admin/routes.js:551 |
++| GET | /api/admin/ai/overviews | server/admin/routes.js:583 |
++| GET | /api/admin/ai/status | server/admin/routes.js:541 |
++| POST | /api/admin/ai/streamer/:userId/overview | server/admin/routes.js:592 |
++| GET | /api/admin/ai/usage | server/admin/routes.js:494 |
++| POST | /api/admin/ai/viewers/:userId/:action | server/admin/routes.js:522 |
++| GET | /api/admin/ai/viewers/:userId/log | server/admin/routes.js:532 |
++| GET | /api/admin/ai/viewers/status | server/admin/routes.js:504 |
++| GET | /api/admin/analytics | server/index.js:708 |
++| GET | /api/admin/analytics/bots | server/index.js:717 |
++| GET | /api/admin/bans | server/admin/routes.js:429 |
++| POST | /api/admin/broadcast | server/index.js:826 |
++| PUT | /api/admin/channels/:id/force-nsfw | server/admin/routes.js:359 |
++| GET | /api/admin/diagnostics | server/admin/routes.js:51 |
++| DELETE | /api/admin/media-tools/cookies | server/admin/routes.js:1214 |
++| PUT | /api/admin/media-tools/cookies | server/admin/routes.js:1133 |
++| POST | /api/admin/media-tools/cookies/check | server/admin/routes.js:1192 |
++| DELETE | /api/admin/media-tools/extra-args | server/admin/routes.js:1247 |
++| PUT | /api/admin/media-tools/extra-args | server/admin/routes.js:1228 |
++| GET | /api/admin/media-tools/status | server/admin/routes.js:1088 |
++| POST | /api/admin/media-tools/test | server/admin/routes.js:1258 |
++| GET | /api/admin/moderators | server/admin/routes.js:697 |
++| POST | /api/admin/moderators | server/admin/routes.js:709 |
++| DELETE | /api/admin/moderators/:id | server/admin/routes.js:736 |
++| PUT | /api/admin/pastes/:id/nsfw | server/admin/routes.js:418 |
++| GET | /api/admin/settings | server/admin/routes.js:482 |
++| PUT | /api/admin/settings | server/admin/routes.js:607 |
++| DELETE | /api/admin/settings/:key | server/admin/routes.js:680 |
++| PUT | /api/admin/settings/:key | server/admin/routes.js:663 |
++| GET | /api/admin/stats | server/admin/routes.js:63 |
++| GET | /api/admin/storage | server/admin/routes.js:935 |
++| DELETE | /api/admin/storage/clips/bulk | server/admin/routes.js:1038 |
++| GET | /api/admin/storage/vods | server/admin/routes.js:989 |
++| DELETE | /api/admin/storage/vods/bulk | server/admin/routes.js:1008 |
++| GET | /api/admin/streams | server/admin/routes.js:294 |
++| DELETE | /api/admin/streams/:id | server/admin/routes.js:313 |
++| PUT | /api/admin/streams/:id/nsfw | server/admin/routes.js:407 |
++| GET | /api/admin/users | server/admin/routes.js:111 |
++| PUT | /api/admin/users/:id | server/admin/routes.js:147 |
++| DELETE | /api/admin/users/:id/ban | server/admin/routes.js:275 |
++| POST | /api/admin/users/:id/ban | server/admin/routes.js:237 |
++| PUT | /api/admin/users/:id/force-vod-recording | server/admin/routes.js:376 |
++| GET | /api/admin/verification-keys | server/admin/routes.js:813 |
++| POST | /api/admin/verification-keys | server/admin/routes.js:823 |
++| DELETE | /api/admin/verification-keys/:id | server/admin/routes.js:866 |
++| GET | /api/admin/vpn-queue | server/admin/routes.js:445 |
++| PUT | /api/admin/vpn-queue/:id | server/admin/routes.js:461 |
++| POST | /api/ai-viewers/${cmd} | server/ai/viewers/routes.js:117 |
++| DELETE | /api/ai-viewers/bots/:id | server/ai/viewers/routes.js:220 |
++| PATCH | /api/ai-viewers/bots/:id | server/ai/viewers/routes.js:185 |
++| POST | /api/ai-viewers/bots/:id/clear-memory | server/ai/viewers/routes.js:209 |
++| POST | /api/ai-viewers/bots/:id/mute | server/ai/viewers/routes.js:122 |
++| POST | /api/ai-viewers/bots/:id/unmute | server/ai/viewers/routes.js:123 |
++| POST | /api/ai-viewers/byo/test | server/ai/viewers/routes.js:124 |
++| POST | /api/ai-viewers/clone | server/ai/viewers/routes.js:233 |
++| GET | /api/ai-viewers/config | server/ai/viewers/routes.js:82 |
++| PUT | /api/ai-viewers/config | server/ai/viewers/routes.js:134 |
++| GET | /api/ai-viewers/log | server/ai/viewers/routes.js:106 |
++| POST | /api/ai-viewers/preview | server/ai/viewers/routes.js:282 |
++| GET | /api/ai-viewers/roster | server/ai/viewers/routes.js:167 |
++| GET | /api/ai-viewers/status | server/ai/viewers/routes.js:99 |
++| GET | /api/ai-viewers/threads | server/ai/viewers/routes.js:112 |
++| GET | /api/analytics/channel/:username | server/streaming/analytics-routes.js:22 |
++| GET | /api/analytics/channel/:username/dashboard | server/streaming/analytics-routes.js:156 |
++| GET | /api/analytics/channel/:username/streams | server/streaming/analytics-routes.js:57 |
++| GET | /api/analytics/stream/:id | server/streaming/analytics-routes.js:96 |
++| POST | /api/arena/backfill | server/arena/routes.js:80 |
++| GET | /api/arena/beefs | server/arena/routes.js:112 |
++| GET | /api/arena/beefs/:id | server/arena/routes.js:113 |
++| POST | /api/arena/beefs/:id/hype | server/arena/routes.js:117 |
++| GET | /api/arena/console/:user | server/arena/routes.js:85 |
++| GET | /api/arena/feed | server/arena/routes.js:37 |
++| GET | /api/arena/fighters | server/arena/routes.js:32 |
++| GET | /api/arena/fighters/:user | server/arena/routes.js:56 |
++| POST | /api/arena/fighters/:user/refresh | server/arena/routes.js:69 |
++| GET | /api/arena/fighters/:user/stat/:stat | server/arena/routes.js:46 |
++| GET | /api/arena/levels | server/arena/routes.js:150 |
++| GET | /api/arena/live | server/arena/routes.js:36 |
++| GET | /api/arena/me | server/arena/routes.js:138 |
++| GET | /api/arena/status | server/arena/routes.js:30 |
++| GET | /api/arena/voice/:user | server/arena/routes.js:122 |
++| POST | /api/auth/avatar | server/auth/routes.js:192 |
++| GET | /api/auth/avatar/history | server/auth/routes.js:246 |
++| GET | /api/auth/callback | server/auth/routes.js:488 |
++| POST | /api/auth/change-password | server/auth/routes.js:166 |
++| POST | /api/auth/fedcm | server/auth/routes.js:465 |
++| GET | /api/auth/ice-servers | server/auth/routes.js:898 |
++| POST | /api/auth/login | server/auth/routes.js:97 |
++| GET | /api/auth/logout | server/auth/routes.js:627 |
++| POST | /api/auth/logout | server/auth/routes.js:620 |
++| GET | /api/auth/me | server/auth/routes.js:102 |
++| GET | /api/auth/preferences | server/auth/routes.js:677 |
++| PUT | /api/auth/preferences | server/auth/routes.js:687 |
++| PUT | /api/auth/profile | server/auth/routes.js:110 |
++| POST | /api/auth/refresh | server/auth/routes.js:558 |
++| POST | /api/auth/register | server/auth/routes.js:92 |
++| GET | /api/auth/sso/login | server/auth/routes.js:312 |
++| GET | /api/auth/sso/status | server/auth/routes.js:638 |
++| GET | /api/auth/stream-key | server/auth/routes.js:171 |
++| POST | /api/auth/stream-key/regenerate | server/auth/routes.js:176 |
++| GET | /api/auth/tokens | server/auth/routes.js:857 |
++| POST | /api/auth/tokens | server/auth/routes.js:819 |
++| DELETE | /api/auth/tokens/:id | server/auth/routes.js:877 |
++| GET | /api/auth/user/:username | server/auth/routes.js:183 |
++| GET | /api/channels/:channelId/moderation | server/admin/channel-mod-routes.js:157 |
++| PUT | /api/channels/:channelId/moderation | server/admin/channel-mod-routes.js:170 |
++| GET | /api/channels/:channelId/moderation/chat-search | server/admin/channel-mod-routes.js:306 |
++| GET | /api/channels/:channelId/moderation/logs | server/admin/channel-mod-routes.js:288 |
++| POST | /api/channels/:channelId/moderation/messages/:messageId/delete | server/admin/channel-mod-routes.js:320 |
++| GET | /api/channels/:channelId/mods | server/admin/channel-mod-routes.js:75 |
++| POST | /api/channels/:channelId/mods | server/admin/channel-mod-routes.js:89 |
++| DELETE | /api/channels/:channelId/mods/:userId | server/admin/channel-mod-routes.js:127 |
++| GET | /api/channels/moderation/mine | server/admin/channel-mod-routes.js:49 |
++| GET | /api/chat-ai/anon/:anonId | server/ai/chat-ai-routes.js:83 |
++| GET | /api/chat-ai/global | server/ai/chat-ai-routes.js:14 |
++| GET | /api/chat-ai/live-captions/:username | server/ai/chat-ai-routes.js:230 |
++| GET | /api/chat-ai/relay/:platform/:username | server/ai/chat-ai-routes.js:95 |
++| GET | /api/chat-ai/timeline | server/ai/chat-ai-routes.js:24 |
++| GET | /api/chat-ai/timeline/:username | server/ai/chat-ai-routes.js:174 |
++| GET | /api/chat-ai/transcript/:streamId | server/ai/chat-ai-routes.js:322 |
++| GET | /api/chat-ai/user/:id | server/ai/chat-ai-routes.js:39 |
++| GET | /api/chat-ai/vod-transcripts | server/ai/chat-ai-routes.js:267 |
++| GET | /api/chat/:streamId/history | server/chat/routes.js:509 |
++| GET | /api/chat/:streamId/replay | server/chat/routes.js:487 |
++| GET | /api/chat/:streamId/users | server/chat/routes.js:633 |
++| GET | /api/chat/admin/logs | server/chat/routes.js:718 |
++| GET | /api/chat/admin/logs/export | server/chat/routes.js:755 |
++| DELETE | /api/chat/admin/purge | server/chat/routes.js:674 |
++| POST | /api/chat/admin/purge/preview | server/chat/routes.js:642 |
++| GET | /api/chat/anon/:anonId | server/chat/routes.js:419 |
++| GET | /api/chat/anon/:anonId/logs | server/chat/routes.js:430 |
++| GET | /api/chat/channel/:userId/history | server/chat/routes.js:609 |
++| GET | /api/chat/filters/friendly | server/chat/routes.js:462 |
++| GET | /api/chat/gif/providers | server/chat/routes.js:109 |
++| GET | /api/chat/gif/search | server/chat/routes.js:145 |
++| GET | /api/chat/gif/trending | server/chat/routes.js:119 |
++| GET | /api/chat/global/history | server/chat/routes.js:468 |
++| GET | /api/chat/relay-user/:platform/:username | server/chat/routes.js:409 |
++| GET | /api/chat/relay-user/:platform/:username/logs | server/chat/routes.js:446 |
++| GET | /api/chat/search | server/chat/routes.js:319 |
++| POST | /api/chat/send | server/chat/routes.js:209 |
++| GET | /api/chat/user/:userId/history | server/chat/routes.js:341 |
++| GET | /api/chat/user/:username/profile | server/chat/routes.js:360 |
++| GET | /api/clips | server/media-proxy/clips.js:149 |
++| DELETE | /api/clips/:id | server/media-proxy/clips.js:317 |
++| GET | /api/clips/:id | server/media-proxy/clips.js:178 |
++| POST | /api/clips/:id/recut | server/media-proxy/clips.js:335 |
++| PUT | /api/clips/:id/title | server/media-proxy/clips.js:251 |
++| PUT | /api/clips/:id/visibility | server/media-proxy/clips.js:268 |
++| POST | /api/clips/bulk | server/media-proxy/clips.js:292 |
++| GET | /api/clips/mine | server/media-proxy/clips.js:101 |
++| GET | /api/clips/my-stream | server/media-proxy/clips.js:115 |
++| GET | /api/clips/settings/channel | server/media-proxy/clips.js:127 |
++| PUT | /api/clips/settings/channel | server/media-proxy/clips.js:136 |
++| POST | /api/coins/admin/grant | server/monetization/coins-routes.js:310 |
++| GET | /api/coins/balance | server/monetization/coins-routes.js:26 |
++| POST | /api/coins/bonus | server/monetization/coins-routes.js:70 |
++| GET | /api/coins/channel-balance | server/monetization/coins-routes.js:31 |
++| PUT | /api/coins/config | server/monetization/coins-routes.js:95 |
++| GET | /api/coins/config/:userId | server/monetization/coins-routes.js:90 |
++| POST | /api/coins/heartbeat | server/monetization/coins-routes.js:52 |
++| GET | /api/coins/history | server/monetization/coins-routes.js:44 |
++| GET | /api/coins/rates | server/monetization/coins-routes.js:37 |
++| POST | /api/coins/redeem | server/monetization/coins-routes.js:207 |
++| GET | /api/coins/redemptions | server/monetization/coins-routes.js:261 |
++| POST | /api/coins/redemptions/:id | server/monetization/coins-routes.js:267 |
++| POST | /api/coins/rewards | server/monetization/coins-routes.js:116 |
++| DELETE | /api/coins/rewards/:id | server/monetization/coins-routes.js:193 |
++| PUT | /api/coins/rewards/:id | server/monetization/coins-routes.js:154 |
++| GET | /api/coins/rewards/:userId | server/monetization/coins-routes.js:83 |
++| DELETE | /api/comments/:commentId | server/media-proxy/comments.js:178 |
++| PUT | /api/comments/:commentId | server/media-proxy/comments.js:159 |
++| GET | /api/comments/:commentId/replies | server/media-proxy/comments.js:149 |
++| GET | /api/comments/:type/:id | server/media-proxy/comments.js:40 |
++| POST | /api/comments/:type/:id | server/media-proxy/comments.js:64 |
++| GET | /api/controls/:streamId | server/controls/routes.js:1460 |
++| GET | /api/controls/:streamId/config | server/controls/routes.js:1417 |
++| PUT | /api/controls/:streamId/config | server/controls/routes.js:1431 |
++| DELETE | /api/controls/:streamId/presets/cozmo | server/controls/routes.js:1507 |
++| POST | /api/controls/:streamId/presets/cozmo | server/controls/routes.js:1493 |
++| POST | /api/controls/api-key | server/controls/routes.js:1228 |
++| GET | /api/controls/api-keys | server/controls/routes.js:1255 |
++| GET | /api/controls/configs | server/controls/routes.js:979 |
++| POST | /api/controls/configs | server/controls/routes.js:994 |
++| DELETE | /api/controls/configs/:id | server/controls/routes.js:1053 |
++| GET | /api/controls/configs/:id | server/controls/routes.js:1018 |
++| PUT | /api/controls/configs/:id | server/controls/routes.js:1033 |
++| POST | /api/controls/configs/:id/activate | server/controls/routes.js:1183 |
++| POST | /api/controls/configs/:id/apply/:streamId | server/controls/routes.js:1208 |
++| GET | /api/controls/configs/:id/bridge-script | server/controls/routes.js:1391 |
++| POST | /api/controls/configs/:id/buttons | server/controls/routes.js:1073 |
++| DELETE | /api/controls/configs/:id/buttons/:btnId | server/controls/routes.js:1165 |
++| PUT | /api/controls/configs/:id/buttons/:btnId | server/controls/routes.js:1123 |
++| POST | /api/controls/configs/deactivate | server/controls/routes.js:1198 |
++| GET | /api/controls/cozmo-script | server/controls/routes.js:1373 |
++| GET | /api/controls/settings/channel | server/controls/routes.js:1270 |
++| PUT | /api/controls/settings/channel | server/controls/routes.js:1287 |
++| GET | /api/controls/whitelist | server/controls/routes.js:1325 |
++| POST | /api/controls/whitelist | server/controls/routes.js:1341 |
++| DELETE | /api/controls/whitelist/:id | server/controls/routes.js:1359 |
++| POST | /api/cosmetics/activate | server/monetization/cosmetics-routes.js:64 |
++| GET | /api/cosmetics/catalog | server/monetization/cosmetics-routes.js:20 |
++| POST | /api/cosmetics/deactivate | server/monetization/cosmetics-routes.js:73 |
++| POST | /api/cosmetics/equip | server/monetization/cosmetics-routes.js:46 |
++| GET | /api/cosmetics/equipped/:userId | server/monetization/cosmetics-routes.js:38 |
++| POST | /api/cosmetics/internal-unlock | server/monetization/cosmetics-routes.js:95 |
++| GET | /api/cosmetics/inventory | server/monetization/cosmetics-routes.js:32 |
++| POST | /api/cosmetics/unequip | server/monetization/cosmetics-routes.js:55 |
++| POST | /api/csp-report | server/index.js:847 |
++| GET | /api/dm/blocks | server/chat/dm-routes.js:401 |
++| DELETE | /api/dm/blocks/:userId | server/chat/dm-routes.js:389 |
++| POST | /api/dm/blocks/:userId | server/chat/dm-routes.js:374 |
++| GET | /api/dm/blocks/check/:userId | server/chat/dm-routes.js:411 |
++| GET | /api/dm/conversations | server/chat/dm-routes.js:49 |
++| POST | /api/dm/conversations | server/chat/dm-routes.js:64 |
++| GET | /api/dm/conversations/:id | server/chat/dm-routes.js:120 |
++| PATCH | /api/dm/conversations/:id | server/chat/dm-routes.js:331 |
++| GET | /api/dm/conversations/:id/messages | server/chat/dm-routes.js:137 |
++| POST | /api/dm/conversations/:id/messages | server/chat/dm-routes.js:155 |
++| DELETE | /api/dm/conversations/:id/messages/:msgId | server/chat/dm-routes.js:421 |
++| POST | /api/dm/conversations/:id/participants | server/chat/dm-routes.js:256 |
++| DELETE | /api/dm/conversations/:id/participants/:userId | server/chat/dm-routes.js:310 |
++| POST | /api/dm/conversations/:id/read | server/chat/dm-routes.js:234 |
++| GET | /api/dm/unread | server/chat/dm-routes.js:350 |
++| GET | /api/dm/users/search | server/chat/dm-routes.js:360 |
++| GET | /api/easter-egg/daily | server/ai/easter-egg-routes.js:16 |
++| POST | /api/easter-egg/reveal | server/ai/easter-egg-routes.js:69 |
++| POST | /api/easter-egg/solve | server/ai/easter-egg-routes.js:41 |
++| POST | /api/emotes | server/emotes/routes.js:279 |
++| DELETE | /api/emotes/:id | server/emotes/routes.js:473 |
++| PATCH | /api/emotes/:id | server/emotes/routes.js:416 |
++| GET | /api/emotes/7tv | server/emotes/routes.js:570 |
++| GET | /api/emotes/all/:streamId | server/emotes/routes.js:660 |
++| GET | /api/emotes/bttv | server/emotes/routes.js:552 |
++| GET | /api/emotes/channel/:userId | server/emotes/routes.js:234 |
++| GET | /api/emotes/defaults | server/emotes/routes.js:588 |
++| GET | /api/emotes/ffz | server/emotes/routes.js:533 |
++| GET | /api/emotes/file/:filename | server/emotes/routes.js:505 |
++| GET | /api/emotes/global | server/emotes/routes.js:215 |
++| GET | /api/emotes/mine | server/emotes/routes.js:257 |
++| GET | /api/emotes/search | server/emotes/routes.js:622 |
++| GET | /api/emotes/sources | server/emotes/routes.js:593 |
++| PUT | /api/emotes/sources | server/emotes/routes.js:608 |
++| GET | /api/funds/balance | server/monetization/routes.js:167 |
++| POST | /api/funds/cashout | server/monetization/routes.js:152 |
++| POST | /api/funds/cashout/:id/approve | server/monetization/routes.js:263 |
++| POST | /api/funds/cashout/:id/deny | server/monetization/routes.js:273 |
++| GET | /api/funds/cashouts/pending | server/monetization/routes.js:283 |
++| POST | /api/funds/donate | server/monetization/routes.js:53 |
++| POST | /api/funds/goals | server/monetization/routes.js:209 |
++| DELETE | /api/funds/goals/:id | server/monetization/routes.js:240 |
++| PUT | /api/funds/goals/:id | server/monetization/routes.js:223 |
++| GET | /api/funds/goals/:userId | server/monetization/routes.js:257 |
++| GET | /api/funds/goals/manage/mine | server/monetization/routes.js:204 |
++| GET | /api/funds/history | server/monetization/routes.js:191 |
++| GET | /api/funds/leaderboard/:streamId | server/monetization/routes.js:198 |
++| POST | /api/funds/purchase | server/monetization/routes.js:34 |
++| POST | /api/funds/recycle | server/monetization/routes.js:181 |
++| GET | /api/health | server/index.js:727 |
++| GET | /api/home/digest | server/home/routes.js:457 |
++| GET | /api/home/discover | server/home/routes.js:507 |
++| GET | /api/home/featured | server/home/routes.js:247 |
++| GET | /api/home/hero | server/home/routes.js:333 |
++| GET | /api/home/pulse | server/home/routes.js:406 |
++| GET | /api/home/star | server/home/routes.js:352 |
++| POST | /api/home/star/rotate | server/home/routes.js:569 |
++| GET | /api/home/stats-live | server/home/routes.js:287 |
++| GET | /api/home/stats/series/:metric | server/home/routes.js:177 |
++| GET | /api/i18n/languages | server/i18n/routes.js:31 |
++| POST | /api/i18n/translate | server/i18n/routes.js:36 |
++| GET | /api/img-proxy | server/media/external-image-proxy.js:31 |
++| GET | /api/kiosk/favicon | server/kiosk/routes.js:122 |
++| GET | /api/kiosk/site | server/kiosk/routes.js:77 |
++| GET | /api/live-events | server/index.js:655 |
++| POST | /api/media/advance | server/media/routes.js:262 |
++| GET | /api/media/channel/:username | server/media/routes.js:52 |
++| GET | /api/media/downloader/status | server/media/routes.js:413 |
++| DELETE | /api/media/queue/:id | server/media/routes.js:309 |
++| POST | /api/media/queue/:id/fail | server/media/routes.js:398 |
++| POST | /api/media/queue/:id/move | server/media/routes.js:299 |
++| POST | /api/media/queue/:id/play | server/media/routes.js:273 |
++| POST | /api/media/queue/:id/position | server/media/routes.js:365 |
++| POST | /api/media/queue/:id/refund | server/media/routes.js:383 |
++| POST | /api/media/queue/:id/skip | server/media/routes.js:290 |
++| GET | /api/media/queue/:id/stream-url | server/media/routes.js:320 |
++| POST | /api/media/quote | server/media/routes.js:165 |
++| POST | /api/media/request | server/media/routes.js:217 |
++| GET | /api/media/settings | server/media/routes.js:98 |
++| PUT | /api/media/settings | server/media/routes.js:102 |
++| POST | /api/media/start | server/media/routes.js:253 |
++| GET | /api/meta/openvibeapp | server/meta/routes.js:87 |
++| DELETE | /api/mod/ban/:id | server/admin/mod-routes.js:269 |
++| GET | /api/mod/bans | server/admin/mod-routes.js:42 |
++| GET | /api/mod/chat/search | server/admin/mod-routes.js:315 |
++| GET | /api/mod/chat/user/:userId | server/admin/mod-routes.js:346 |
++| POST | /api/mod/delete-message | server/admin/mod-routes.js:364 |
++| POST | /api/mod/delete-user-messages | server/admin/mod-routes.js:419 |
++| POST | /api/mod/global-ban | server/admin/mod-routes.js:146 |
++| POST | /api/mod/ip-approval/:channelId/approve | server/admin/mod-routes.js:611 |
++| POST | /api/mod/ip-approval/:channelId/deny | server/admin/mod-routes.js:668 |
++| GET | /api/mod/ip-approval/:channelId/pending | server/admin/mod-routes.js:584 |
++| POST | /api/mod/ip-approval/:channelId/review | server/admin/mod-routes.js:698 |
++| GET | /api/mod/ip/alts/:userId | server/admin/mod-routes.js:801 |
++| GET | /api/mod/ip/anon/:anonId | server/admin/mod-routes.js:755 |
++| POST | /api/mod/ip/ban-all | server/admin/mod-routes.js:813 |
++| GET | /api/mod/ip/log | server/admin/mod-routes.js:853 |
++| GET | /api/mod/ip/lookup/:ip | server/admin/mod-routes.js:786 |
++| GET | /api/mod/ip/user/:userId | server/admin/mod-routes.js:730 |
++| DELETE | /api/mod/relay-user/:id | server/admin/mod-routes.js:542 |
++| POST | /api/mod/relay-user/hide | server/admin/mod-routes.js:484 |
++| GET | /api/mod/relay-users/hidden/:channelId | server/admin/mod-routes.js:567 |
++| POST | /api/mod/stream-ban | server/admin/mod-routes.js:197 |
++| GET | /api/mod/tts-voice/:kind/:id | server/admin/mod-routes.js:889 |
++| PUT | /api/mod/tts-voice/:kind/:id | server/admin/mod-routes.js:910 |
++| POST | /api/mod/tts-voice/preview | server/admin/mod-routes.js:940 |
++| DELETE | /api/mod/users/:id/ban | server/admin/mod-routes.js:169 |
++| POST | /api/mod/users/:id/ban | server/admin/mod-routes.js:159 |
++| GET | /api/news/my-settings | server/news/news-routes.js:71 |
++| PUT | /api/news/my-settings | server/news/news-routes.js:77 |
++| GET | /api/news/sources | server/news/news-routes.js:35 |
++| PUT | /api/news/sources/:id | server/news/news-routes.js:43 |
++| GET | /api/onvif/cameras | server/controls/onvif-routes.js:182 |
++| POST | /api/onvif/cameras | server/controls/onvif-routes.js:105 |
++| DELETE | /api/onvif/cameras/:cameraId/presets/:presetId | server/controls/onvif-routes.js:419 |
++| DELETE | /api/onvif/cameras/:id | server/controls/onvif-routes.js:324 |
++| GET | /api/onvif/cameras/:id | server/controls/onvif-routes.js:210 |
++| PUT | /api/onvif/cameras/:id | server/controls/onvif-routes.js:263 |
++| GET | /api/onvif/cameras/:id/presets | server/controls/onvif-routes.js:349 |
++| POST | /api/onvif/cameras/:id/presets | server/controls/onvif-routes.js:372 |
++| POST | /api/onvif/discover | server/controls/onvif-routes.js:70 |
++| GET | /api/pastes | server/media-proxy/pastes.js:167 |
++| POST | /api/pastes | server/media-proxy/pastes.js:168 |
++| DELETE | /api/pastes/:slug | server/media-proxy/pastes.js:217 |
++| GET | /api/pastes/:slug | server/media-proxy/pastes.js:215 |
++| PUT | /api/pastes/:slug | server/media-proxy/pastes.js:216 |
++| POST | /api/pastes/:slug/censor | server/media-proxy/pastes.js:222 |
++| GET | /api/pastes/:slug/comments | server/media-proxy/pastes.js:243 |
++| POST | /api/pastes/:slug/comments | server/media-proxy/pastes.js:246 |
++| DELETE | /api/pastes/:slug/comments/:commentId | server/media-proxy/pastes.js:248 |
++| POST | /api/pastes/:slug/copy | server/media-proxy/pastes.js:242 |
++| POST | /api/pastes/:slug/fork | server/media-proxy/pastes.js:238 |
++| POST | /api/pastes/:slug/like | server/media-proxy/pastes.js:241 |
++| GET | /api/pastes/:slug/raw | server/media-proxy/pastes.js:240 |
++| POST | /api/pastes/:slug/set-avatar | server/media-proxy/pastes.js:110 |
++| DELETE | /api/pastes/admin/forks | server/media-proxy/pastes.js:171 |
++| GET | /api/pastes/admin/stats | server/media-proxy/pastes.js:170 |
++| POST | /api/pastes/bulk | server/media-proxy/pastes.js:172 |
++| GET | /api/pastes/by-user/:username | server/media-proxy/pastes.js:177 |
++| GET | /api/pastes/config | server/media-proxy/pastes.js:169 |
++| POST | /api/pastes/screenshot | server/media-proxy/pastes.js:83 |
++| POST | /api/payments/bucks/checkout | server/monetization/payments-routes.js:24 |
++| GET | /api/payments/channel/:username | server/monetization/payments-routes.js:192 |
++| GET | /api/payments/config | server/monetization/payments-routes.js:19 |
++| GET | /api/payments/paypal/return | server/monetization/payments-routes.js:82 |
++| POST | /api/payments/subscribe | server/monetization/payments-routes.js:102 |
++| POST | /api/payments/subscriptions/:id/cancel | server/monetization/payments-routes.js:208 |
++| GET | /api/payments/subscriptions/mine | server/monetization/payments-routes.js:187 |
++| ALL | /api/payments/webhook/ccbill | server/monetization/payments-routes.js:280 |
++| POST | /api/payments/webhook/crypto | server/monetization/payments-routes.js:299 |
++| POST | /api/payments/webhook/paypal | server/monetization/payments-routes.js:263 |
++| POST | /api/payments/webhook/stripe | server/monetization/payments-routes.js:233 |
++| GET | /api/powerchat/authorize-url | server/integrations/powerchat-routes.js:436 |
++| GET | /api/powerchat/donate-link | server/integrations/powerchat-routes.js:313 |
++| GET | /api/powerchat/oauth/callback | server/integrations/powerchat-routes.js:199 |
++| DELETE | /api/powerchat/oauth/connection | server/integrations/powerchat-routes.js:275 |
++| GET | /api/powerchat/oauth/start | server/integrations/powerchat-routes.js:182 |
++| POST | /api/powerchat/reconcile | server/integrations/powerchat-routes.js:339 |
++| GET | /api/powerchat/status | server/integrations/powerchat-routes.js:86 |
++| POST | /api/powerchat/test-alert | server/integrations/powerchat-routes.js:389 |
++| POST | /api/powerchat/test-tip | server/integrations/powerchat-routes.js:350 |
++| GET | /api/powerchat/tip-link | server/integrations/powerchat-routes.js:291 |
++| POST | /api/powerchat/webhook | server/integrations/powerchat-routes.js:442 |
++| GET | /api/ready | server/index.js:750 |
++| GET | /api/recap/:streamId | server/recap/routes.js:21 |
++| POST | /api/recap/:streamId/regenerate | server/recap/routes.js:43 |
++| GET | /api/recap/channel/:username | server/recap/routes.js:14 |
++| GET | /api/restream/destinations | server/streaming/restream-routes.js:157 |
++| POST | /api/restream/destinations | server/streaming/restream-routes.js:179 |
++| DELETE | /api/restream/destinations/:id | server/streaming/restream-routes.js:341 |
++| PUT | /api/restream/destinations/:id | server/streaming/restream-routes.js:253 |
++| POST | /api/restream/destinations/:id/start | server/streaming/restream-routes.js:364 |
++| POST | /api/restream/destinations/:id/stop | server/streaming/restream-routes.js:420 |
++| GET | /api/restream/oauth/:platform/callback | server/streaming/restream-routes.js:599 |
++| DELETE | /api/restream/oauth/:platform/connection | server/streaming/restream-routes.js:693 |
++| GET | /api/restream/oauth/:platform/start | server/streaming/restream-routes.js:585 |
++| GET | /api/restream/oauth/status | server/streaming/restream-routes.js:563 |
++| GET | /api/restream/presets | server/streaming/restream-routes.js:147 |
++| GET | /api/restream/status | server/streaming/restream-routes.js:440 |
++| GET | /api/restream/viewer-config | server/streaming/restream-routes.js:489 |
++| GET | /api/restream/viewer-counts | server/streaming/restream-routes.js:479 |
++| POST | /api/restream/viewer-counts | server/streaming/restream-routes.js:461 |
++| DELETE | /api/robotstreamer/integration | server/integrations/routes.js:226 |
++| GET | /api/robotstreamer/integration | server/integrations/routes.js:41 |
++| PUT | /api/robotstreamer/integration | server/integrations/routes.js:152 |
++| POST | /api/robotstreamer/integration/login | server/integrations/routes.js:107 |
++| POST | /api/robotstreamer/integration/validate | server/integrations/routes.js:70 |
++| POST | /api/robotstreamer/restream/start | server/integrations/routes.js:194 |
++| POST | /api/robotstreamer/restream/stop | server/integrations/routes.js:214 |
++| POST | /api/sounds | server/chat/sounds-routes.js:147 |
++| DELETE | /api/sounds/:id | server/chat/sounds-routes.js:287 |
++| DELETE | /api/sounds/alert/:kind | server/chat/sounds-routes.js:440 |
++| POST | /api/sounds/alert/:kind | server/chat/sounds-routes.js:403 |
++| GET | /api/sounds/alert/mine | server/chat/sounds-routes.js:393 |
++| GET | /api/sounds/all/:streamId | server/chat/sounds-routes.js:136 |
++| GET | /api/sounds/channel/:userId | server/chat/sounds-routes.js:125 |
++| PATCH | /api/sounds/command | server/chat/sounds-routes.js:313 |
++| GET | /api/sounds/file/:filename | server/chat/sounds-routes.js:371 |
++| GET | /api/streams | server/streaming/routes.js:1084 |
++| POST | /api/streams | server/streaming/routes.js:1886 |
++| DELETE | /api/streams/:id | server/streaming/routes.js:2081 |
++| GET | /api/streams/:id | server/streaming/routes.js:1838 |
++| PUT | /api/streams/:id | server/streaming/routes.js:2030 |
++| GET | /api/streams/:id/call | server/streaming/routes.js:2384 |
++| PUT | /api/streams/:id/call | server/streaming/routes.js:2345 |
++| GET | /api/streams/:id/endpoint | server/streaming/routes.js:2128 |
++| POST | /api/streams/:id/follow | server/streaming/routes.js:2253 |
++| POST | /api/streams/:id/heartbeat | server/streaming/routes.js:2214 |
++| GET | /api/streams/:id/rtmp-status | server/streaming/routes.js:2232 |
++| GET | /api/streams/broadcast-settings | server/streaming/routes.js:1383 |
++| PUT | /api/streams/broadcast-settings | server/streaming/routes.js:1400 |
++| GET | /api/streams/channel | server/streaming/routes.js:636 |
++| PUT | /api/streams/channel | server/streaming/routes.js:648 |
++| GET | /api/streams/channel/:username | server/streaming/routes.js:205 |
++| PUT | /api/streams/channel/:username/about | server/streaming/routes.js:763 |
++| GET | /api/streams/channel/:username/bio-en | server/streaming/routes.js:744 |
++| GET | /api/streams/channel/:username/clips-taken | server/streaming/routes.js:555 |
++| POST | /api/streams/channel/:username/follow | server/streaming/routes.js:2298 |
++| GET | /api/streams/channel/:username/live | server/streaming/routes.js:598 |
++| GET | /api/streams/channel/:username/popular | server/streaming/routes.js:529 |
++| GET | /api/streams/channel/:username/resolve/:ref | server/streaming/routes.js:1422 |
++| GET | /api/streams/channel/:username/weather | server/streaming/routes.js:973 |
++| POST | /api/streams/channel/offline-screen | server/streaming/routes.js:869 |
++| POST | /api/streams/diag-log | server/streaming/routes.js:2207 |
++| POST | /api/streams/goal-media | server/streaming/routes.js:834 |
++| GET | /api/streams/managed | server/streaming/routes.js:1567 |
++| POST | /api/streams/managed | server/streaming/routes.js:1579 |
++| DELETE | /api/streams/managed/:id | server/streaming/routes.js:1793 |
++| PUT | /api/streams/managed/:id | server/streaming/routes.js:1633 |
++| POST | /api/streams/managed/:id/regenerate-key | server/streaming/routes.js:1818 |
++| GET | /api/streams/managed/:managedStreamId/history | server/streaming/routes.js:1456 |
++| GET | /api/streams/managed/:managedStreamId/profile | server/streaming/routes.js:1470 |
++| GET | /api/streams/mine | server/streaming/routes.js:1120 |
++| POST | /api/streams/panel-image | server/streaming/routes.js:812 |
++| GET | /api/streams/recent | server/streaming/routes.js:1132 |
++| GET | /api/streams/recent-vods | server/streaming/routes.js:1202 |
++| GET | /api/streams/recently-online | server/streaming/routes.js:1148 |
++| GET | /api/streams/rtmp-proxy/:streamId.flv | server/streaming/routes.js:2405 |
++| GET | /api/streams/setup-progress | server/streaming/routes.js:1513 |
++| GET | /api/streams/voice-channels | server/streaming/routes.js:1232 |
++| POST | /api/streams/voice-channels | server/streaming/routes.js:1253 |
++| DELETE | /api/streams/voice-channels/:channelId | server/streaming/routes.js:1265 |
++| GET | /api/streams/voice-channels/:channelId | server/streaming/routes.js:1242 |
++| POST | /api/streams/voice-channels/call-user | server/streaming/routes.js:1277 |
++| POST | /api/streams/voice-channels/call-user/respond | server/streaming/routes.js:1346 |
++| GET | /api/themes | server/themes/routes.js:20 |
++| POST | /api/themes | server/themes/routes.js:108 |
++| DELETE | /api/themes/:id | server/themes/routes.js:147 |
++| PUT | /api/themes/:id | server/themes/routes.js:134 |
++| POST | /api/themes/:id/download | server/themes/routes.js:158 |
++| GET | /api/themes/:idOrSlug | server/themes/routes.js:88 |
++| GET | /api/themes/me | server/themes/routes.js:46 |
++| PUT | /api/themes/me | server/themes/routes.js:73 |
++| GET | /api/thumbnails/:filename | server/media-proxy/thumbnails.js:84 |
++| GET | /api/thumbnails/generate/clip/:id | server/media-proxy/thumbnails.js:80 |
++| POST | /api/thumbnails/generate/clip/:id | server/media-proxy/thumbnails.js:67 |
++| GET | /api/thumbnails/generate/vod/:id | server/media-proxy/thumbnails.js:79 |
++| POST | /api/thumbnails/generate/vod/:id | server/media-proxy/thumbnails.js:66 |
++| POST | /api/thumbnails/live/:streamId | server/media-proxy/thumbnails.js:29 |
++| GET | /api/tts/admin/settings | server/chat/tts-routes.js:64 |
++| PUT | /api/tts/admin/settings | server/chat/tts-routes.js:81 |
++| POST | /api/tts/admin/test | server/chat/tts-routes.js:110 |
++| GET | /api/tts/audio/:file | server/chat/tts-routes.js:125 |
++| GET | /api/tts/settings | server/chat/tts-routes.js:43 |
++| GET | /api/tts/voices | server/chat/tts-routes.js:33 |
++| GET | /api/updates | server/index.js:813 |
++| GET | /api/vibe-coding/channel/:username/:slotIdOrSlug/events | server/vibe-coding/routes.js:17 |
++| GET | /api/vibe-coding/managed/:managedStreamId/events | server/vibe-coding/routes.js:47 |
++| GET | /api/vibe-coding/managed/:managedStreamId/settings | server/vibe-coding/routes.js:74 |
++| PUT | /api/vibe-coding/managed/:managedStreamId/settings | server/vibe-coding/routes.js:91 |
++| GET | /api/vods | server/media-proxy/vods.js:270 |
++| DELETE | /api/vods/:id | server/media-proxy/vods.js:545 |
++| GET | /api/vods/:id | server/media-proxy/vods.js:429 |
++| PUT | /api/vods/:id | server/media-proxy/vods.js:500 |
++| GET | /api/vods/:id/context | server/media-proxy/vods.js:368 |
++| GET | /api/vods/:id/live-info | server/media-proxy/vods.js:248 |
++| GET | /api/vods/:id/memories | server/media-proxy/vods.js:344 |
++| POST | /api/vods/:id/publish | server/media-proxy/vods.js:556 |
++| POST | /api/vods/bulk | server/media-proxy/vods.js:517 |
++| POST | /api/vods/bulk-delete-old | server/media-proxy/vods.js:300 |
++| POST | /api/vods/clips | server/media-proxy/vods.js:616 |
++| POST | /api/vods/clips/:id/trim | server/media-proxy/vods.js:771 |
++| GET | /api/vods/clips/stream/:streamId | server/media-proxy/vods.js:760 |
++| GET | /api/vods/file/:filename | server/media-proxy/vods.js:572 |
++| GET | /api/vods/mine | server/media-proxy/vods.js:288 |
++| POST | /api/vods/stream/:streamId/chunk | server/media-proxy/vods.js:154 |
++| POST | /api/vods/stream/:streamId/finalize | server/media-proxy/vods.js:208 |
++| GET | /api/vods/stream/:streamId/live | server/media-proxy/vods.js:228 |
++| POST | /api/vods/upload | server/media-proxy/vods.js:578 |
++| GET | /banned | server/index.js:981 |
++| POST | /banned/continue | server/index.js:951 |
++| GET | /canvas | server/index.js:683 |
++| GET | /data/pastes/screenshots/:filename | server/index.js:606 |
++| GET | /dmca | server/index.js:890 |
++| GET | /docs | server/docs/routes.js:304 |
++| GET | /docs/:name | server/docs/routes.js:306 |
++| GET | /docs/:name.md | server/docs/routes.js:305 |
++| GET | /game | server/index.js:682 |
++| POST | /internal/ai/site-copy | server/internal/routes.js:25 |
++| GET | /internal/analytics-summary | server/index.js:578 |
++| POST | /internal/media-webhook | server/index.js:573 |
++| POST | /internal/url-registry/refresh | server/internal/routes.js:49 |
++| POST | /internal/user-avatar | server/internal/routes.js:67 |
++| POST | /internal/user-role | server/internal/routes.js:91 |
++| GET | /media/:username | server/index.js:885 |
++| GET | /obs/chat/:username | server/index.js:873 |
++| GET | /openvibe-sw.js | server/index.js:474 |
++| GET | /overlay/chat/:username | server/index.js:881 |
++| GET | /overlay/chat/:username/:slotIdOrSlug | server/index.js:878 |
++| GET | /p/:slug | server/index.js:624 |
++| GET | /p/:slug/raw | server/index.js:635 |
++| GET | /p/:slug/screenshot | server/index.js:631 |
++| GET | /pastes | server/index.js:625 |
++| GET | /privacy | server/index.js:899 |
++| GET | /sitemap.xml | server/seo/seo.js:594 |
++| GET | /terms | server/index.js:896 |
++| GET | /tos | server/index.js:893 |
++| POST | /whip/:streamId | server/index.js:908 |
++| DELETE | /whip/:streamId/:resourceId | server/index.js:910 |
++| PATCH | /whip/:streamId/:resourceId | server/index.js:909 |
++
++## OpenVibe.Network
++
++| Method | Path | Source |
++|---|---|---|
++| DELETE | / | server/notifications/routes.js:111 |
++| GET | / | server/discord/routes.js:17 |
++| GET | / | server/notifications/routes.js:16 |
++| PUT | / | server/discord/routes.js:33 |
++| POST | /:id/dismiss | server/notifications/routes.js:100 |
++| POST | /:id/read | server/notifications/routes.js:67 |
++| GET | /.well-known/web-identity | server/index.js:572 |
++| POST | /admin | server/setup/routes.js:114 |
++| GET | /api/.well-known/jwks | server/index.js:356 |
++| GET | /api/admin/domains | server/domains/routes.js:176 |
++| GET | /api/admin/domains | server/domains/routes.js:197 |
++| POST | /api/admin/domains | server/domains/routes.js:224 |
++| DELETE | /api/admin/domains/:id | server/domains/routes.js:256 |
++| PUT | /api/admin/domains/:id | server/domains/routes.js:238 |
++| GET | /api/admin/domains/catalog | server/domains/routes.js:206 |
++| POST | /api/admin/domains/check | server/domains/routes.js:217 |
++| GET | /api/admin/ssh-info | server/index.js:452 |
++| GET | /api/auth/anon-identities | server/auth/routes.js:635 |
++| POST | /api/auth/anon-session | server/auth/routes.js:512 |
++| GET | /api/auth/anon/:token | server/auth/routes.js:582 |
++| POST | /api/auth/anon/:token/link | server/auth/routes.js:619 |
++| PUT | /api/auth/anon/:token/preferences | server/auth/routes.js:596 |
++| POST | /api/auth/change-password | server/auth/routes.js:422 |
++| GET | /api/auth/discord/callback | server/auth/discord-link.js:61 |
++| DELETE | /api/auth/discord/link | server/auth/discord-link.js:158 |
++| GET | /api/auth/discord/link | server/auth/discord-link.js:31 |
++| GET | /api/auth/discord/status | server/auth/discord-link.js:178 |
++| POST | /api/auth/email/send-verification | server/auth/email-verify.js:99 |
++| GET | /api/auth/email/status | server/auth/email-verify.js:83 |
++| POST | /api/auth/email/verify | server/auth/email-verify.js:122 |
++| POST | /api/auth/forgot-password | server/auth/routes.js:206 |
++| POST | /api/auth/login | server/auth/routes.js:183 |
++| POST | /api/auth/logout | server/auth/routes.js:506 |
++| GET | /api/auth/me | server/auth/routes.js:362 |
++| PUT | /api/auth/profile | server/auth/routes.js:372 |
++| POST | /api/auth/refresh | server/auth/routes.js:300 |
++| POST | /api/auth/register | server/auth/routes.js:88 |
++| POST | /api/auth/reset-password | server/auth/routes.js:254 |
++| GET | /api/auth/reset-password/validate | server/auth/routes.js:238 |
++| DELETE | /api/auth/sessions | server/auth/routes.js:717 |
++| GET | /api/auth/sessions | server/auth/routes.js:673 |
++| POST | /api/auth/sessions | server/auth/routes.js:686 |
++| DELETE | /api/auth/sessions/:id | server/auth/routes.js:708 |
++| GET | /api/auth/users/:id/card | server/auth/routes.js:728 |
++| DELETE | /api/auth/users/:id/follow | server/auth/routes.js:811 |
++| POST | /api/auth/users/:id/follow | server/auth/routes.js:780 |
++| GET | /api/brand | server/index.js:373 |
++| GET | /api/catalog.json | server/index.js:423 |
++| GET | /api/chrome | server/chrome/service.js:137 |
++| POST | /api/chrome/hit | server/chrome/service.js:150 |
++| GET | /api/domains | server/domains/routes.js:176 |
++| GET | /api/domains | server/domains/routes.js:197 |
++| POST | /api/domains | server/domains/routes.js:224 |
++| DELETE | /api/domains/:id | server/domains/routes.js:256 |
++| PUT | /api/domains/:id | server/domains/routes.js:238 |
++| GET | /api/domains/catalog | server/domains/routes.js:206 |
++| POST | /api/domains/check | server/domains/routes.js:217 |
++| GET | /api/health | server/index.js:368 |
++| DELETE | /api/history | server/history/routes.js:143 |
++| GET | /api/history | server/history/routes.js:92 |
++| POST | /api/history | server/history/routes.js:108 |
++| DELETE | /api/history/:id | server/history/routes.js:148 |
++| PUT | /api/history/settings | server/history/routes.js:137 |
++| DELETE | /api/profile/avatar | server/profile/avatar.js:107 |
++| PUT | /api/profile/avatar | server/profile/avatar.js:96 |
++| GET | /api/profile/avatar/:username | server/profile/avatar.js:110 |
++| PUT | /api/push/live-preferences | server/push/routes.js:60 |
++| GET | /api/push/status | server/push/routes.js:37 |
++| POST | /api/push/subscribe | server/push/routes.js:17 |
++| POST | /api/push/unsubscribe | server/push/routes.js:29 |
++| GET | /api/push/vapid-key | server/push/routes.js:10 |
++| GET | /api/sso/targets | server/index.js:405 |
++| GET | /api/themes | server/themes/routes.js:41 |
++| POST | /api/themes | server/themes/routes.js:148 |
++| GET | /api/themes/:idOrSlug | server/themes/routes.js:109 |
++| PUT | /api/themes/me | server/themes/routes.js:119 |
++| GET | /api/themes/me/active | server/themes/routes.js:74 |
++| PUT | /api/themes/me/display | server/themes/routes.js:99 |
++| POST | /api/webhooks/resend | server/notifications/resend-webhook.js:26 |
++| GET | /audit | server/admin/routes.js:797 |
++| DELETE | /avatar | server/profile/avatar.js:107 |
++| PUT | /avatar | server/profile/avatar.js:96 |
++| GET | /avatar/:username | server/profile/avatar.js:110 |
++| POST | /bootstrap | server/setup/routes.js:99 |
++| GET | /bots | server/admin/analytics-routes.js:255 |
++| POST | /broadcast | server/admin/routes.js:721 |
++| GET | /certs | server/deploy/routes.js:139 |
++| POST | /certs/issue-cloudflare | server/deploy/routes.js:164 |
++| POST | /certs/issue-manual | server/deploy/routes.js:236 |
++| POST | /certs/manual-info | server/deploy/routes.js:215 |
++| POST | /certs/renew | server/deploy/routes.js:281 |
++| GET | /config | server/deploy/routes.js:81 |
++| PUT | /config | server/deploy/routes.js:95 |
++| POST | /deploy | server/setup/routes.js:239 |
++| GET | /deploy-status | server/setup/routes.js:309 |
++| GET | /domains | server/deploy/routes.js:406 |
++| PUT | /domains | server/deploy/routes.js:426 |
++| GET | /email | server/admin/routes.js:240 |
++| PUT | /email | server/admin/routes.js:250 |
++| POST | /email/test | server/admin/routes.js:283 |
++| GET | /fedcm/accounts | server/auth/fedcm.js:111 |
++| POST | /fedcm/assertion | server/auth/fedcm.js:125 |
++| GET | /fedcm/client-metadata | server/auth/fedcm.js:120 |
++| GET | /fedcm/config.json | server/auth/fedcm.js:97 |
++| POST | /fedcm/disconnect | server/auth/fedcm.js:142 |
++| GET | /health | server/admin/routes.js:766 |
++| POST | /identity | server/setup/routes.js:152 |
++| GET | /internal/anon-admin | server/internal/routes.js:618 |
++| GET | /internal/anon-list | server/internal/routes.js:667 |
++| POST | /internal/audit | server/internal/routes.js:156 |
++| POST | /internal/coins/credit | server/internal/routes.js:242 |
++| POST | /internal/coins/debit | server/internal/routes.js:254 |
++| GET | /internal/coins/stats | server/internal/routes.js:204 |
++| POST | /internal/coins/transfer | server/internal/routes.js:267 |
++| POST | /internal/events/stream-live | server/internal/routes.js:284 |
++| POST | /internal/issue-token | server/internal/routes.js:508 |
++| POST | /internal/link-account | server/internal/routes.js:107 |
++| POST | /internal/notifications/mark-read | server/internal/routes.js:469 |
++| POST | /internal/notifications/push | server/internal/routes.js:411 |
++| POST | /internal/notifications/push-bulk | server/internal/routes.js:431 |
++| POST | /internal/notifications/resolve-users | server/internal/routes.js:488 |
++| GET | /internal/notifications/unread/:userId | server/internal/routes.js:454 |
++| POST | /internal/resolve-anon | server/internal/routes.js:539 |
++| GET | /internal/stats | server/internal/routes.js:165 |
++| GET | /internal/url-registry/resolved | server/internal/routes.js:174 |
++| POST | /internal/user-avatar | server/internal/routes.js:141 |
++| GET | /internal/users/:id | server/internal/routes.js:58 |
++| GET | /internal/users/:id/linked-accounts | server/internal/routes.js:149 |
++| GET | /internal/users/:id/theme | server/internal/routes.js:95 |
++| POST | /internal/users/bulk | server/internal/routes.js:80 |
++| GET | /internal/users/by-username/:username | server/internal/routes.js:69 |
++| POST | /internal/verify-token | server/internal/routes.js:36 |
++| GET | /llms.txt | server/index.js:550 |
++| GET | /me | server/coins/routes.js:18 |
++| GET | /me/history | server/coins/routes.js:24 |
++| GET | /net-config | server/admin/routes.js:907 |
++| PUT | /net-config | server/admin/routes.js:926 |
++| GET | /newest | server/notifications/routes.js:57 |
++| POST | /nginx/apply | server/deploy/routes.js:343 |
++| GET | /nginx/preview | server/deploy/routes.js:296 |
++| GET | /nginx/preview/:serviceId | server/deploy/routes.js:314 |
++| POST | /nginx/reload | server/deploy/routes.js:382 |
++| POST | /nginx/validate | server/deploy/routes.js:330 |
++| GET | /oauth/.well-known/openid-configuration | server/auth/oauth-routes.js:304 |
++| GET | /oauth/authorize | server/auth/oauth-routes.js:20 |
++| POST | /oauth/confirm | server/auth/oauth-routes.js:78 |
++| POST | /oauth/token | server/auth/oauth-routes.js:131 |
++| GET | /openvibe-sw.js | server/index.js:596 |
++| GET | /overview | server/admin/analytics-routes.js:75 |
++| GET | /preferences | server/notifications/routes.js:121 |
++| PUT | /preferences | server/notifications/routes.js:132 |
++| DELETE | /preferences/:category | server/notifications/routes.js:149 |
++| GET | /prerequisites | server/deploy/routes.js:57 |
++| POST | /read-all | server/notifications/routes.js:89 |
++| POST | /read-batch | server/notifications/routes.js:78 |
++| GET | /realtime | server/admin/analytics-routes.js:285 |
++| POST | /reinit | server/discord/routes.js:96 |
++| POST | /reset-db | server/admin/routes.js:473 |
++| GET | /service/:name | server/admin/analytics-routes.js:224 |
++| GET | /settings | server/admin/routes.js:300 |
++| PUT | /settings | server/admin/routes.js:328 |
++| GET | /sso/check | server/index.js:577 |
++| GET | /status | server/setup/routes.js:91 |
++| POST | /test | server/discord/routes.js:77 |
++| POST | /test-live | server/discord/routes.js:87 |
++| GET | /tos | server/index.js:419 |
++| GET | /unread-by-category | server/notifications/routes.js:47 |
++| GET | /unread-count | server/notifications/routes.js:37 |
++| GET | /url-registry | server/admin/routes.js:353 |
++| PUT | /url-registry | server/admin/routes.js:380 |
++| DELETE | /url-registry/:key | server/admin/routes.js:423 |
++| PUT | /url-registry/:key | server/admin/routes.js:401 |
++| POST | /url-registry/:key/reset | server/admin/routes.js:458 |
++| POST | /url-registry/refresh-all | server/admin/routes.js:438 |
++| POST | /urls | server/setup/routes.js:193 |
++| GET | /users | server/admin/routes.js:536 |
++| PUT | /users/:id/ban | server/admin/routes.js:691 |
++| PUT | /users/:id/email | server/admin/routes.js:568 |
++| PUT | /users/:id/role | server/admin/routes.js:666 |
++| POST | /users/:id/send-reset | server/admin/routes.js:607 |
++| POST | /users/grant-admin | server/admin/routes.js:500 |
++| GET | /verification-keys | server/admin/routes.js:824 |
++| POST | /verification-keys | server/admin/routes.js:833 |
++| DELETE | /verification-keys/:id | server/admin/routes.js:881 |
++| GET | /verify-email | server/index.js:562 |
++
++## OpenVibe.Media
++
++| Method | Path | Source |
++|---|---|---|
++| GET | /api/v1/:app/admin/storage | server/admin/routes.js:72 |
++| GET | /api/v1/:app/admin/storage/buckets | server/admin/routes.js:403 |
++| GET | /api/v1/:app/admin/storage/tiers | server/admin/routes.js:257 |
++| POST | /api/v1/:app/admin/storage/tiers/bulk-move | server/admin/routes.js:376 |
++| POST | /api/v1/:app/admin/storage/tiers/move | server/admin/routes.js:358 |
++| PUT | /api/v1/:app/admin/storage/tiers/settings | server/admin/routes.js:305 |
++| POST | /api/v1/:app/admin/storage/tiers/sweep | server/admin/routes.js:331 |
++| GET | /api/v1/:app/admin/storage/vods | server/admin/routes.js:127 |
++| DELETE | /api/v1/:app/admin/storage/vods/bulk | server/admin/routes.js:199 |
++| GET | /api/v1/:app/assets | server/assets/routes.js:72 |
++| POST | /api/v1/:app/assets | server/assets/routes.js:39 |
++| DELETE | /api/v1/:app/assets/:id | server/assets/routes.js:84 |
++| GET | /api/v1/:app/clips | server/vod/clips-routes.js:283 |
++| POST | /api/v1/:app/clips | server/vod/clips-routes.js:114 |
++| DELETE | /api/v1/:app/clips/:id | server/vod/clips-routes.js:363 |
++| GET | /api/v1/:app/clips/:id | server/vod/clips-routes.js:309 |
++| PUT | /api/v1/:app/clips/:id | server/vod/clips-routes.js:345 |
++| POST | /api/v1/:app/clips/:id/recut | server/vod/clips-routes.js:328 |
++| GET | /api/v1/:app/files | server/files/routes.js:139 |
++| POST | /api/v1/:app/files | server/files/routes.js:75 |
++| DELETE | /api/v1/:app/files/:key | server/files/routes.js:167 |
++| GET | /api/v1/:app/files/:key | server/files/routes.js:156 |
++| GET | /api/v1/:app/pastes | server/pastes/routes.js:248 |
++| POST | /api/v1/:app/pastes | server/pastes/routes.js:462 |
++| DELETE | /api/v1/:app/pastes/:slug | server/pastes/routes.js:616 |
++| GET | /api/v1/:app/pastes/:slug | server/pastes/routes.js:436 |
++| PUT | /api/v1/:app/pastes/:slug | server/pastes/routes.js:578 |
++| POST | /api/v1/:app/pastes/:slug/ai | server/pastes/routes.js:558 |
++| POST | /api/v1/:app/pastes/:slug/censor | server/pastes/routes.js:397 |
++| GET | /api/v1/:app/pastes/:slug/comments | server/pastes/routes.js:702 |
++| POST | /api/v1/:app/pastes/:slug/comments | server/pastes/routes.js:725 |
++| DELETE | /api/v1/:app/pastes/:slug/comments/:commentId | server/pastes/routes.js:803 |
++| POST | /api/v1/:app/pastes/:slug/copy | server/pastes/routes.js:677 |
++| POST | /api/v1/:app/pastes/:slug/fork | server/pastes/routes.js:634 |
++| POST | /api/v1/:app/pastes/:slug/like | server/pastes/routes.js:660 |
++| DELETE | /api/v1/:app/pastes/admin/forks | server/pastes/routes.js:359 |
++| GET | /api/v1/:app/pastes/admin/forks | server/pastes/routes.js:339 |
++| GET | /api/v1/:app/pastes/admin/stats | server/pastes/routes.js:329 |
++| POST | /api/v1/:app/pastes/bulk | server/pastes/routes.js:372 |
++| GET | /api/v1/:app/pastes/config | server/pastes/routes.js:307 |
++| GET | /api/v1/:app/stats | server/index.js:93 |
++| GET | /api/v1/:app/stats/series/:metric | server/index.js:103 |
++| POST | /api/v1/:app/thumbnails/:kind/:id | server/thumbnails/routes.js:38 |
++| GET | /api/v1/:app/views | server/views/routes.js:39 |
++| POST | /api/v1/:app/views | server/views/routes.js:24 |
++| GET | /api/v1/:app/vods | server/vod/routes.js:266 |
++| POST | /api/v1/:app/vods | server/vod/routes.js:89 |
++| DELETE | /api/v1/:app/vods/:id | server/vod/routes.js:353 |
++| GET | /api/v1/:app/vods/:id | server/vod/routes.js:305 |
++| PUT | /api/v1/:app/vods/:id | server/vod/routes.js:331 |
++| POST | /api/v1/:app/vods/:id/chunks | server/vod/routes.js:155 |
++| POST | /api/v1/:app/vods/:id/complete | server/vod/routes.js:259 |
++| POST | /api/v1/:app/vods/:id/finalize | server/vod/routes.js:260 |
++| POST | /api/v1/:app/vods/:id/ingest/rtmp | server/vod/routes.js:112 |
++| POST | /api/v1/:app/vods/:id/ingest/rtp/start | server/vod/routes.js:126 |
++| POST | /api/v1/:app/vods/:id/ingest/rtp/stop | server/vod/routes.js:140 |
++| GET | /api/v1/:app/vods/latest-thumbs | server/vod/routes.js:294 |
++| GET | /auth/callback | server/user-auth.js:281 |
++| POST | /auth/fedcm | server/user-auth.js:327 |
++| GET | /auth/login | server/user-auth.js:259 |
++| GET | /auth/logout | server/user-auth.js:355 |
++| GET | /auth/me | server/user-auth.js:379 |
++| POST | /auth/refresh | server/user-auth.js:389 |
++| GET | /healthz | server/index.js:82 |
++| POST | /internal/avatar-ingest | server/index.js:56 |
++| GET | /manifest.webmanifest | server/index.js:44 |
++| GET | /tos | server/index.js:41 |
++
++## OpenVibe.Tools
++
++| Method | Path | Source |
++|---|---|---|
++| GET | / | apps/gateway/server/pages/site.js:249 |
++| GET | / | apps/text/server/index.js:194 |
++| GET | /:slug | apps/gateway/server/pages/site.js:257 |
++| GET | /all-tools | apps/gateway/server/pages/site.js:250 |
++| GET | /api/brand | apps/gateway/server/index.js:132 |
++| GET | /api/catalog.json | apps/gateway/server/pages/site.js:248 |
++| GET | /api/context | apps/audio/server/index.js:97 |
++| GET | /api/context | apps/docs/server/index.js:94 |
++| GET | /api/context | apps/img/server/index.js:95 |
++| GET | /api/dev/opengraph | apps/gateway/server/dev/routes.js:91 |
++| GET | /api/dev/tools | apps/gateway/server/dev/routes.js:79 |
++| POST | /api/dev/webhook/bins | apps/gateway/server/dev/routes.js:151 |
++| DELETE | /api/dev/webhook/bins/:binId | apps/gateway/server/dev/routes.js:217 |
++| GET | /api/dev/webhook/bins/:binId | apps/gateway/server/dev/routes.js:172 |
++| ALL | /api/dev/webhook/bins/:binId/in | apps/gateway/server/dev/routes.js:186 |
++| POST | /api/download | apps/yt/server/index.js:121 |
++| DELETE | /api/download/:id | apps/yt/server/index.js:202 |
++| GET | /api/download/:id | apps/audio/server/index.js:195 |
++| GET | /api/download/:id | apps/docs/server/index.js:236 |
++| GET | /api/download/:id | apps/img/server/index.js:209 |
++| GET | /api/download/:id | apps/yt/server/index.js:209 |
++| GET | /api/food-banks | apps/food/server/index.js:81 |
++| GET | /api/food-banks | apps/maps/server/index.js:374 |
++| GET | /api/foods | apps/food/server/index.js:83 |
++| GET | /api/foods | apps/maps/server/index.js:395 |
++| GET | /api/geocode | apps/food/server/index.js:85 |
++| GET | /api/geocode | apps/maps/server/index.js:97 |
++| GET | /api/health | apps/audio/server/index.js:91 |
++| GET | /api/health | apps/docs/server/index.js:88 |
++| GET | /api/health | apps/gateway/server/index.js:128 |
++| GET | /api/health | apps/img/server/index.js:89 |
++| GET | /api/health | apps/text/server/index.js:167 |
++| GET | /api/health | apps/yt/server/index.js:99 |
++| POST | /api/info | apps/docs/server/index.js:116 |
++| POST | /api/info | apps/yt/server/index.js:106 |
++| GET | /api/internal/analytics | apps/audio/server/index.js:244 |
++| GET | /api/internal/analytics | apps/docs/server/index.js:254 |
++| GET | /api/internal/analytics | apps/food/server/index.js:88 |
++| GET | /api/internal/analytics | apps/gateway/server/index.js:220 |
++| GET | /api/internal/analytics | apps/img/server/index.js:227 |
++| GET | /api/internal/analytics | apps/maps/server/index.js:417 |
++| GET | /api/internal/analytics | apps/text/server/index.js:155 |
++| GET | /api/internal/analytics | apps/yt/server/index.js:223 |
++| GET | /api/internal/analytics/bots | apps/audio/server/index.js:249 |
++| GET | /api/internal/analytics/bots | apps/docs/server/index.js:259 |
++| GET | /api/internal/analytics/bots | apps/food/server/index.js:93 |
++| GET | /api/internal/analytics/bots | apps/img/server/index.js:232 |
++| GET | /api/internal/analytics/bots | apps/maps/server/index.js:422 |
++| GET | /api/internal/analytics/bots | apps/text/server/index.js:160 |
++| GET | /api/internal/analytics/bots | apps/yt/server/index.js:228 |
++| GET | /api/meal-plan | apps/food/server/index.js:84 |
++| GET | /api/meal-plan | apps/maps/server/index.js:405 |
++| GET | /api/net/dns/:target? | apps/gateway/server/net/routes.js:273 |
++| GET | /api/net/headers/:target? | apps/gateway/server/net/routes.js:489 |
++| GET | /api/net/ip/:target? | apps/gateway/server/net/routes.js:97 |
++| GET | /api/net/ipv4/:target? | apps/gateway/server/net/routes.js:151 |
++| GET | /api/net/ipv6/:target? | apps/gateway/server/net/routes.js:225 |
++| GET | /api/net/lookup/:target? | apps/gateway/server/net/routes.js:695 |
++| GET | /api/net/myip | apps/gateway/server/net/routes.js:90 |
++| GET | /api/net/ping/:target? | apps/gateway/server/net/routes.js:639 |
++| GET | /api/net/port/:target? | apps/gateway/server/net/routes.js:590 |
++| GET | /api/net/rdap/:target? | apps/gateway/server/net/routes.js:361 |
++| GET | /api/net/rdns/:target? | apps/gateway/server/net/routes.js:333 |
++| GET | /api/net/redirects/:target? | apps/gateway/server/net/routes.js:539 |
++| GET | /api/net/ssl/:target? | apps/gateway/server/net/routes.js:409 |
++| GET | /api/net/tools | apps/gateway/server/net/routes.js:82 |
++| GET | /api/net/whois/:target? | apps/gateway/server/net/routes.js:398 |
++| GET | /api/preview/:id | apps/audio/server/index.js:213 |
++| POST | /api/probe | apps/audio/server/index.js:119 |
++| POST | /api/process | apps/audio/server/index.js:132 |
++| POST | /api/process | apps/docs/server/index.js:128 |
++| POST | /api/process | apps/img/server/index.js:117 |
++| POST | /api/process/direct | apps/img/server/index.js:174 |
++| POST | /api/process/multi | apps/docs/server/index.js:189 |
++| GET | /api/search | apps/maps/server/index.js:203 |
++| GET | /api/search/stream | apps/maps/server/index.js:248 |
++| GET | /api/status/:id | apps/yt/server/index.js:136 |
++| GET | /api/status/:id/stream | apps/yt/server/index.js:152 |
++| GET | /api/stores | apps/food/server/index.js:82 |
++| GET | /api/stores | apps/maps/server/index.js:385 |
++| GET | /api/terrain | apps/maps/server/index.js:364 |
++| GET | /api/tools | apps/audio/server/index.js:114 |
++| GET | /api/tools | apps/docs/server/index.js:111 |
++| GET | /api/tools | apps/img/server/index.js:112 |
++| GET | /api/weather | apps/maps/server/index.js:348 |
++| GET | /auth/fedcm/callback | apps/gateway/server/auth/routes.js:331 |
++| POST | /auth/fedcm/fedcm | apps/gateway/server/auth/routes.js:381 |
++| GET | /auth/fedcm/login | apps/gateway/server/auth/routes.js:303 |
++| GET | /auth/fedcm/logout | apps/gateway/server/auth/routes.js:431 |
++| GET | /auth/fedcm/me | apps/gateway/server/auth/routes.js:458 |
++| POST | /auth/fedcm/refresh | apps/gateway/server/auth/routes.js:469 |
++| GET | /llms.txt | apps/gateway/server/pages/site.js:255 |
++| GET | /openvibe-sw.js | apps/gateway/server/index.js:285 |
++| GET | /robots.txt | apps/gateway/server/pages/site.js:254 |
++| GET | /search | apps/gateway/server/pages/site.js:251 |
++| GET | /sitemap.xml | apps/gateway/server/pages/site.js:253 |
++| GET | /sitemap.xml | apps/text/server/index.js:188 |
++| GET | /tool/:id | apps/gateway/server/pages/site.js:252 |
++| GET | /tos | apps/gateway/server/index.js:245 |
++
++## OpenVibe.Community
++
++| Method | Path | Source |
++|---|---|---|
++| GET | / | server/app.js:106 |
++| GET | /api/health | server/app.js:81 |
++| GET | /api/ready | server/app.js:82 |
++| GET | /callback | server/auth/routes.js:281 |
++| POST | /fedcm | server/auth/routes.js:327 |
++| GET | /feed.xml | server/app.js:99 |
++| GET | /login | server/auth/routes.js:259 |
++| GET | /logout | server/auth/routes.js:355 |
++| GET | /me | server/auth/routes.js:379 |
++| GET | /my | server/app.js:186 |
++| GET | /new | server/app.js:150 |
++| POST | /new | server/app.js:159 |
++| GET | /p/:slug | server/app.js:119 |
++| GET | /p/:slug/download | server/app.js:138 |
++| GET | /p/:slug/raw | server/app.js:135 |
++| GET | /p/:slug/screenshot | server/app.js:136 |
++| GET | /pastes | server/app.js:111 |
++| GET | /pastes/:slug | server/app.js:117 |
++| POST | /refresh | server/auth/routes.js:389 |
++| GET | /robots.txt | server/app.js:97 |
++| GET | /sitemap.xml | server/app.js:98 |
++| GET | /tos | server/app.js:75 |
++
++## OpenVibe.Games
++
++| Method | Path | Source |
++|---|---|---|
++| ANY | / | apps/server/src/net/httpServer.ts:616 |
++| ANY | /api/characters | apps/server/src/net/httpServer.ts:302 |
++| POST | /api/map | apps/server/src/net/httpServer.ts:151 |
++| POST | /api/map-assets | apps/server/src/net/httpServer.ts:221 |
++| POST | /api/texture | apps/server/src/net/httpServer.ts:221 |
++| ANY | /auth/* | apps/server/src/net/httpServer.ts:322 |
++| ANY | /auth/callback | apps/server/src/net/httpServer.ts:388 |
++| ANY | /auth/fedcm | apps/server/src/net/httpServer.ts:474 |
++| ANY | /auth/login | apps/server/src/net/httpServer.ts:337 |
++| ANY | /auth/logout | apps/server/src/net/httpServer.ts:549 |
++| ANY | /auth/me | apps/server/src/net/httpServer.ts:566 |
++| ANY | /editor | apps/server/src/net/httpServer.ts:620 |
++| ANY | /editor.html | apps/server/src/net/httpServer.ts:603 |
++| ANY | /healthz | apps/server/src/net/httpServer.ts:584 |
++| ANY | /map-assets/* | apps/server/src/net/httpServer.ts:276 |
++| ANY | /map.json | apps/server/src/net/httpServer.ts:138 |
++| ANY | /metrics | apps/server/src/net/httpServer.ts:589 |
++| ANY | /play | apps/server/src/net/httpServer.ts:610 |
++| ANY | /play | apps/server/src/net/httpServer.ts:618 |
++| ANY | /play.html | apps/server/src/net/httpServer.ts:603 |
++| ANY | /play.html | apps/server/src/net/httpServer.ts:605 |
ddiff --git a/docs/roadmap-baseline/04-cross-service.md b/docs/roadmap-baseline/04-cross-service.md
nnew file mode 100644
iindex 00000000..c0d59159
---- /dev/null
++++ b/docs/roadmap-baseline/04-cross-service.md
@@@ -0,0 +1,101 @@
++# Cross-service calls
++
++> Generated by `scripts/roadmap-baseline/generate.js`. Do not edit by hand; edit `data/*.json` or the extractor and regenerate.
++
++Deliverable 3 (dependency half). A call site is a source file that makes HTTP requests and names another service by internal URL env var or loopback port. Auth, timeout and retry are detected per file, so treat them as leads to confirm.
++
++## Dependency graph
++
++| Caller | Callee | Files | Auth mechanisms |
++|---|---|---|---|
++| OpenVibe.Community | OpenVibe.Live | 2 | bearer |
++| OpenVibe.Community | OpenVibe.Media | 1 | bearer |
++| OpenVibe.Community | OpenVibe.Network | 5 | bearer, internal-key, oauth-client |
++| OpenVibe.Games | OpenVibe.Network | 2 | bearer, oauth-client |
++| OpenVibe.Live | OpenVibe.Community | 1 | none-detected |
++| OpenVibe.Live | OpenVibe.Media | 2 | api-key, bearer, internal-key |
++| OpenVibe.Live | OpenVibe.Network | 12 | bearer, hmac, internal-key, oauth-client |
++| OpenVibe.Live | webhook | 2 | hmac, none-detected |
++| OpenVibe.Media | OpenVibe.Live | 1 | none-detected |
++| OpenVibe.Media | OpenVibe.Network | 6 | bearer, internal-key, none-detected, oauth-client |
++| OpenVibe.Media | webhook | 2 | hmac, none-detected |
++| OpenVibe.Network | OpenVibe.Games | 3 | bearer, internal-key |
++| OpenVibe.Network | OpenVibe.Live | 5 | bearer, internal-key |
++| OpenVibe.Network | OpenVibe.Media | 4 | bearer, internal-key |
++| OpenVibe.Network | OpenVibe.Tools | 4 | bearer, internal-key, none-detected |
++| OpenVibe.Sites | OpenVibe.Network | 1 | bearer |
++| OpenVibe.Tools | OpenVibe.Community | 1 | bearer, internal-key |
++| OpenVibe.Tools | OpenVibe.Live | 1 | bearer, internal-key |
++| OpenVibe.Tools | OpenVibe.Network | 10 | bearer, internal-key, none-detected, oauth-client |
++
++## Call sites
++
++| Caller file | Callee | Via | Auth | Timeout | Retry |
++|---|---|---|---|---|---|
++| Community/server/live-client.js:6 | OpenVibe.Live | liveInternalUrl | bearer | yes | no |
++| Community/server/pastes/proxy.js:26 | OpenVibe.Live | liveUrl, liveInternalUrl | bearer | yes | yes |
++| Community/server/live-client.js:86 | OpenVibe.Media | mediaUrl | bearer | yes | no |
++| Community/server/auth/routes.js:49 | OpenVibe.Network | /api/.well-known/jwks, networkUrl, networkInternalUrl, /oauth/token | oauth-client | yes | yes |
++| Community/vendor/openvibe-shared/account-switcher.js:176 | OpenVibe.Network | /api/auth/me | bearer | **no** | yes |
++| Community/vendor/openvibe-shared/auth-client.js:23 | OpenVibe.Network | :4000, /oauth/token | internal-key, oauth-client | **no** | no |
++| Community/vendor/openvibe-shared/footer.js:280 | OpenVibe.Network | /api/auth/me | bearer | **no** | no |
++| Community/vendor/openvibe-shared/navbar.js:10 | OpenVibe.Network | /api/auth/me | bearer | **no** | yes |
++| Games/apps/server/src/net/httpServer.ts:335 | OpenVibe.Network | /api/auth/me, /oauth/token | oauth-client | **no** | yes |
++| Games/apps/server/src/net/networkAuth.ts:3 | OpenVibe.Network | /api/auth/me | bearer | **no** | no |
++| Live/server/seo/seo.js:424 | OpenVibe.Community | OV_COMMUNITY_URL | none-detected | **no** | no |
++| Live/server/config.js:175 | OpenVibe.Media | MEDIA_URL, :4100 | internal-key | yes | no |
++| Live/server/media-client.js:14 | OpenVibe.Media | :4100, MEDIA_URL, /api/v1/${ | api-key, bearer | yes | no |
++| Live/server/chat/chat-server.js:84 | OpenVibe.Network | OV_NETWORK_INTERNAL_URL | internal-key | **no** | no |
++| Live/server/config.js:21 | OpenVibe.Network | :4000, OV_NETWORK_URL | internal-key | yes | no |
++| Live/server/integrations/platform-oauth.js:52 | OpenVibe.Network | /oauth/token | bearer, hmac, oauth-client | **no** | yes |
++| Live/server/integrations/powerchat-oauth.js:51 | OpenVibe.Network | /oauth/token | bearer, hmac, oauth-client | **no** | yes |
++| Live/server/monetization/wallet-client.js:9 | OpenVibe.Network | /internal/coins, OV_NETWORK_INTERNAL_URL, :4000 | internal-key, bearer | yes | no |
++| Live/server/streaming/golive-notify.js:60 | OpenVibe.Network | /internal/events | internal-key | **no** | no |
++| Live/server/streaming/restream-manager.js:1387 | OpenVibe.Network | /oauth/token | bearer, oauth-client | yes | yes |
++| Live/server/utils/notify.js:16 | OpenVibe.Network | OV_NETWORK_INTERNAL_URL, :4000, /internal/notifications | internal-key | **no** | no |
++| Live/vendor/openvibe-shared/account-switcher.js:176 | OpenVibe.Network | /api/auth/me | bearer | **no** | yes |
++| Live/vendor/openvibe-shared/auth-client.js:23 | OpenVibe.Network | :4000, /oauth/token | internal-key, oauth-client | **no** | no |
++| Live/vendor/openvibe-shared/footer.js:280 | OpenVibe.Network | /api/auth/me | bearer | **no** | no |
++| Live/vendor/openvibe-shared/navbar.js:10 | OpenVibe.Network | /api/auth/me | bearer | **no** | yes |
++| Live/server/integrations/discord-webhook.js:16 | webhook | webhook_url (per-app, configured in DB) | none-detected | **no** | no |
++| Live/server/media-proxy/webhook.js:38 | webhook | webhook_url (per-app, configured in DB) | hmac | yes | no |
++| Media/server/thumbnails/live-frame-service.js:170 | OpenVibe.Live | :3000 | none-detected | yes | yes |
++| Media/server/auth.js:28 | OpenVibe.Network | network.url, /api/.well-known/jwks | none-detected | yes | yes |
++| Media/server/user-auth.js:49 | OpenVibe.Network | /api/.well-known/jwks, networkUrl, networkInternalUrl, /oauth/token | oauth-client | yes | yes |
++| Media/vendor/openvibe-shared/account-switcher.js:176 | OpenVibe.Network | /api/auth/me | bearer | **no** | yes |
++| Media/vendor/openvibe-shared/auth-client.js:23 | OpenVibe.Network | :4000, /oauth/token | internal-key, oauth-client | **no** | no |
++| Media/vendor/openvibe-shared/footer.js:280 | OpenVibe.Network | /api/auth/me | bearer | **no** | no |
++| Media/vendor/openvibe-shared/navbar.js:10 | OpenVibe.Network | /api/auth/me | bearer | **no** | yes |
++| Media/server/auth.js:268 | webhook | webhook_url (per-app, configured in DB) | none-detected | yes | yes |
++| Media/server/webhooks.js:4 | webhook | webhook_url (per-app, configured in DB) | hmac | yes | yes |
++| Network/server/admin/analytics-routes.js:24 | OpenVibe.Games | :8000 | internal-key, bearer | yes | no |
++| Network/server/admin/routes.js:71 | OpenVibe.Games | games.internalUrl | internal-key | yes | no |
++| Network/server/index.js:219 | OpenVibe.Games | :8000, games.internalUrl | internal-key, bearer | **no** | no |
++| Network/server/admin/analytics-routes.js:22 | OpenVibe.Live | :3000 | internal-key, bearer | yes | no |
++| Network/server/admin/routes.js:69 | OpenVibe.Live | live.internalUrl, OV_LIVE_INTERNAL_URL, :3000 | internal-key | yes | no |
++| Network/server/chrome/service.js:46 | OpenVibe.Live | :3000 | internal-key | yes | no |
++| Network/server/index.js:215 | OpenVibe.Live | :3000, live.internalUrl | internal-key, bearer | **no** | no |
++| Network/server/profile/avatar.js:64 | OpenVibe.Live | live.internalUrl, :3000 | internal-key | yes | no |
++| Network/server/admin/analytics-routes.js:25 | OpenVibe.Media | :4100 | internal-key, bearer | yes | no |
++| Network/server/admin/routes.js:72 | OpenVibe.Media | media.internalUrl | internal-key | yes | no |
++| Network/server/index.js:218 | OpenVibe.Media | :4100, media.internalUrl | internal-key, bearer | **no** | no |
++| Network/server/profile/avatar.js:72 | OpenVibe.Media | media.internalUrl, :4100 | internal-key | yes | no |
++| Network/server/admin/analytics-routes.js:23 | OpenVibe.Tools | :4001 | internal-key, bearer | yes | no |
++| Network/server/admin/routes.js:70 | OpenVibe.Tools | tools.internalUrl | internal-key | yes | no |
++| Network/server/domains/catalog.js:17 | OpenVibe.Tools | OV_TOOLS_INTERNAL_URL, :4001 | none-detected | yes | no |
++| Network/server/index.js:217 | OpenVibe.Tools | :4001, tools.internalUrl | internal-key, bearer | **no** | no |
++| Sites/vendor/openvibe-shared/footer.js:280 | OpenVibe.Network | /api/auth/me | bearer | **no** | no |
++| Tools/apps/gateway/server/index.js:266 | OpenVibe.Community | OV_COMMUNITY_URL | internal-key, bearer | yes | no |
++| Tools/apps/gateway/server/index.js:148 | OpenVibe.Live | liveUrl | internal-key, bearer | yes | no |
++| Tools/apps/audio/server/auth.js:9 | OpenVibe.Network | /api/.well-known/jwks, networkUrl, networkInternalUrl | none-detected | yes | yes |
++| Tools/apps/docs/server/auth.js:9 | OpenVibe.Network | /api/.well-known/jwks, networkUrl, networkInternalUrl | none-detected | yes | yes |
++| Tools/apps/gateway/server/auth/routes.js:53 | OpenVibe.Network | /oauth/token, /api/.well-known/jwks, networkUrl, networkInternalUrl | oauth-client | yes | yes |
++| Tools/apps/gateway/server/index.js:317 | OpenVibe.Network | networkUrl | internal-key, bearer | yes | no |
++| Tools/apps/img/server/auth.js:9 | OpenVibe.Network | /api/.well-known/jwks, networkUrl, networkInternalUrl | none-detected | yes | yes |
++| Tools/apps/yt/server/auth.js:9 | OpenVibe.Network | /api/.well-known/jwks, networkUrl, networkInternalUrl | none-detected | yes | yes |
++| Tools/vendor/openvibe-shared/account-switcher.js:176 | OpenVibe.Network | /api/auth/me | bearer | **no** | yes |
++| Tools/vendor/openvibe-shared/auth-client.js:23 | OpenVibe.Network | :4000, /oauth/token | internal-key, oauth-client | **no** | no |
++| Tools/vendor/openvibe-shared/footer.js:280 | OpenVibe.Network | /api/auth/me | bearer | **no** | no |
++| Tools/vendor/openvibe-shared/navbar.js:10 | OpenVibe.Network | /api/auth/me | bearer | **no** | yes |
++
++Inbound webhooks and internal endpoints are in [03-routes.md](03-routes.md) under `/internal/*` and `/api/webhooks/*`.
ddiff --git a/docs/roadmap-baseline/05-realtime-and-jobs.md b/docs/roadmap-baseline/05-realtime-and-jobs.md
nnew file mode 100644
iindex 00000000..e8190ac6
---- /dev/null
++++ b/docs/roadmap-baseline/05-realtime-and-jobs.md
@@@ -0,0 +1,134 @@
++# WebSockets, protocols and background jobs
++
++> Generated by `scripts/roadmap-baseline/generate.js`. Do not edit by hand; edit `data/*.json` or the extractor and regenerate.
++
++Deliverable 4, plus the background-job half of the exit criteria.
++
++## WebSocket servers
++
++| Repo | Constructed at |
++|---|---|
++| OpenVibe.Live | server/chat/chat-server.js:244 |
++| OpenVibe.Live | server/controls/control-server.js:32 |
++| OpenVibe.Live | server/integrations/robotstreamer-service.js:42 |
++| OpenVibe.Live | server/streaming/broadcast-server.js:130 |
++| OpenVibe.Live | server/streaming/call-server.js:62 |
++| OpenVibe.Live | server/streaming/jsmpeg-relay.js:53 |
++| OpenVibe.Live | server/streaming/jsmpeg-relay.js:99 |
++| OpenVibe.Live | server/vibe-coding/publish-server.js:23 |
++| OpenVibe.Games | apps/server/src/net/editorWs.ts:53 |
++| OpenVibe.Games | apps/server/src/net/wsTransport.ts:19 |
++
++## WebSocket paths referenced
++
++| Repo | Path | Referenced at |
++|---|---|---|
++| OpenVibe.Live | /ws/ | server/index.js:418, server/index.js:428, server/index.js:1002 |
++| OpenVibe.Live | /ws/broadcast | server/index.js:1044, server/streaming/broadcast-server.js:187 |
++| OpenVibe.Live | /ws/call | server/index.js:1048, server/streaming/call-server.js:98 |
++| OpenVibe.Live | /ws/canvas | server/index.js:1050 |
++| OpenVibe.Live | /ws/chat | server/chat/chat-server.js:294, server/index.js:1040 |
++| OpenVibe.Live | /ws/control | server/controls/control-server.js:46, server/index.js:1046 |
++| OpenVibe.Live | /ws/game | server/index.js:1050 |
++| OpenVibe.Live | /ws/robotstreamer-publish | server/index.js:1052, server/integrations/robotstreamer-service.js:807 |
++| OpenVibe.Live | /ws/vibe-coding/publish | server/index.js:1042, server/vibe-coding/publish-server.js:31 |
++| OpenVibe.Network | /ws/ | server/deploy/nginx-generator.js:150 |
++| OpenVibe.Games | /ws | apps/server/src/main.ts:129 |
++
++## Non-HTTP protocols
++
++| Service | Protocol | Port(s) |
++|---|---|---|
++| live | rtmp | 1935, 9935 |
++| live | mediasoup | 11000-11300/udp |
++| live | jsmpegRelayTls | 9710-9789 |
++| media | rtpIngest | 12000-12199/udp (127.0.0.1) |
++
++## Background jobs and timers
++
++`jobs.every` / `jobs.singleFlight` are Live's guarded job runner (`server/utils/jobs.js`); `setInterval` rows are raw timers.
++
++| Repo | Kind | Name | Interval | At |
++|---|---|---|---|---|
++| OpenVibe.Live | setInterval | | 5 min | server/ai/ai-moments-job.js:493 |
++| OpenVibe.Live | setInterval | | ? | server/ai/auto-clip-job.js:312 |
++| OpenVibe.Live | setInterval | | 1 min | server/ai/backfill-job.js:160 |
++| OpenVibe.Live | setInterval | | ? | server/ai/chat-ai.js:473 |
++| OpenVibe.Live | setInterval | | 5 min | server/ai/easter-egg-job.js:215 |
++| OpenVibe.Live | setInterval | | ? | server/ai/slogan-job.js:190 |
++| OpenVibe.Live | setInterval | | 30 s | server/ai/stream-memory-job.js:239 |
++| OpenVibe.Live | setInterval | | ? | server/ai/streamer-overview-job.js:40 |
++| OpenVibe.Live | setInterval | | ? | server/ai/timeline-job.js:205 |
++| OpenVibe.Live | setInterval | | ? | server/ai/viewers/engine.js:79 |
++| OpenVibe.Live | setInterval | | ? | server/arena/arena-job.js:33 |
++| OpenVibe.Live | setInterval | | ? | server/arena/arena-job.js:39 |
++| OpenVibe.Live | setInterval | | ? | server/arena/listener.js:323 |
++| OpenVibe.Live | setInterval | | ? | server/chat/chat-server.js:250 |
++| OpenVibe.Live | setInterval | | ? | server/chat/chat-server.js:272 |
++| OpenVibe.Live | setInterval | | ? | server/chat/chat-server.js:277 |
++| OpenVibe.Live | setInterval | | ? | server/chat/dm-routes.js:39 |
++| OpenVibe.Live | setInterval | | ? | server/diagnostics.js:18 |
++| OpenVibe.Live | setInterval | | 5 min | server/home/routes.js:583 |
++| OpenVibe.Live | setInterval | | ? | server/home/star-job.js:130 |
++| OpenVibe.Live | setInterval | | 360 min | server/index.js:1322 |
++| OpenVibe.Live | setInterval | | 45 s | server/index.js:1339 |
++| OpenVibe.Live | setInterval | | ? | server/index.js:1363 |
++| OpenVibe.Live | setInterval | | ? | server/index.js:1374 |
++| OpenVibe.Live | setInterval | | ? | server/integrations/chat-relay-service.js:615 |
++| OpenVibe.Live | setInterval | | 1 s | server/integrations/powerchat-platform.js:296 |
++| OpenVibe.Live | setInterval | | ? | server/integrations/powerchat-platform.js:430 |
++| OpenVibe.Live | setInterval | | ? | server/integrations/powerchat-platform.js:538 |
++| OpenVibe.Live | setInterval | | ? | server/integrations/robotstreamer-service.js:1023 |
++| OpenVibe.Live | setInterval | | ? | server/integrations/rs-passthrough-relay.js:537 |
++| OpenVibe.Live | setInterval | | ? | server/integrations/rs-passthrough-relay.js:755 |
++| OpenVibe.Live | setInterval | | ? | server/integrations/rs-passthrough-relay.js:965 |
++| OpenVibe.Live | setInterval | | 60 min | server/media/media-downloader.js:550 |
++| OpenVibe.Live | setInterval | | ? | server/media/media-downloader.js:553 |
++| OpenVibe.Live | setInterval | | ? | server/media-proxy/asset-sync.js:119 |
++| OpenVibe.Live | setInterval | | ? | server/media-proxy/clip-notify.js:95 |
++| OpenVibe.Live | setInterval | | 60 min | server/monetization/payments.js:380 |
++| OpenVibe.Live | setInterval | | 30 s | server/news/news-service.js:94 |
++| OpenVibe.Live | setInterval | | ? | server/news/sources/base-source.js:28 |
++| OpenVibe.Live | setInterval | | ? | server/recap/recap.js:236 |
++| OpenVibe.Live | setInterval | | ? | server/streaming/broadcast-server.js:133 |
++| OpenVibe.Live | setInterval | | ? | server/streaming/call-server.js:64 |
++| OpenVibe.Live | setInterval | | 5 min | server/streaming/call-server.js:76 |
++| OpenVibe.Live | setInterval | | ? | server/streaming/live-events.js:28 |
++| OpenVibe.Live | setInterval | | ? | server/streaming/rtmp-server.js:181 |
++| OpenVibe.Live | setInterval | | ? | server/streaming/whip-handler.js:161 |
++| OpenVibe.Live | jobs.every | restream-viewer-counts | 1 min | server/utils/jobs.js:12 |
++| OpenVibe.Live | jobs.singleFlight | auto-clip-backfill | ? | server/utils/jobs.js:13 |
++| OpenVibe.Network | setInterval | | ? | server/auth/discord-link.js:20 |
++| OpenVibe.Network | setInterval | | ? | server/chrome/service.js:167 |
++| OpenVibe.Network | setInterval | | ? | server/chrome/service.js:169 |
++| OpenVibe.Network | setInterval | | 60 min | server/index.js:652 |
++| OpenVibe.Network | setInterval | | 2 min | server/index.js:655 |
++| OpenVibe.Network | setInterval | | ? | server/index.js:658 |
++| OpenVibe.Network | setInterval | | 15 min | packages/openvibe-shared/account-switcher.js:624 |
++| OpenVibe.Network | setInterval | | 5 s | packages/openvibe-shared/analytics.js:238 |
++| OpenVibe.Network | setInterval | | 60 min | packages/openvibe-shared/analytics.js:241 |
++| OpenVibe.Network | setInterval | | ? | packages/openvibe-shared/analytics.js:247 |
++| OpenVibe.Network | setInterval | | ? | packages/openvibe-shared/notification-ui.js:548 |
++| OpenVibe.Media | setInterval | | ? | server/auth.js:58 |
++| OpenVibe.Media | setInterval | | 720 min | server/index.js:115 |
++| OpenVibe.Media | setInterval | | ? | server/index.js:141 |
++| OpenVibe.Media | setInterval | | ? | server/pastes/routes.js:694 |
++| OpenVibe.Media | setInterval | | ? | server/vod/clip-cutter.js:111 |
++| OpenVibe.Media | setInterval | | ? | server/vod/clip-jobs.js:117 |
++| OpenVibe.Media | setInterval | | ? | server/vod/health-job.js:262 |
++| OpenVibe.Media | setInterval | | 1 min | server/vod/recorder.js:213 |
++| OpenVibe.Media | setInterval | | ? | server/vod/vod-storage.js:466 |
++| OpenVibe.Tools | setInterval | | ? | apps/audio/server/auth.js:60 |
++| OpenVibe.Tools | setInterval | | ? | apps/audio/server/retention/manager.js:193 |
++| OpenVibe.Tools | setInterval | | ? | apps/docs/server/auth.js:60 |
++| OpenVibe.Tools | setInterval | | ? | apps/docs/server/retention/manager.js:145 |
++| OpenVibe.Tools | setInterval | | 5 min | apps/gateway/server/dev/routes.js:25 |
++| OpenVibe.Tools | setInterval | | 1 min | apps/gateway/server/registry/index.js:191 |
++| OpenVibe.Tools | setInterval | | ? | apps/img/server/auth.js:60 |
++| OpenVibe.Tools | setInterval | | ? | apps/img/server/retention/manager.js:146 |
++| OpenVibe.Tools | setInterval | | ? | apps/yt/server/auth.js:60 |
++| OpenVibe.Tools | setInterval | | ? | apps/yt/server/downloader.js:555 |
++| OpenVibe.Tools | setInterval | | 5 s | apps/yt/server/downloader.js:606 |
++| OpenVibe.Tools | setInterval | | 0.5 s | apps/yt/server/index.js:196 |
++| OpenVibe.Games | setInterval | | ? | apps/server/src/main.ts:159 |
++| OpenVibe.Games | setInterval | | ? | apps/server/src/net/editorWs.ts:82 |
ddiff --git a/docs/roadmap-baseline/06-secrets.md b/docs/roadmap-baseline/06-secrets.md
nnew file mode 100644
iindex 00000000..120fc5ef
---- /dev/null
++++ b/docs/roadmap-baseline/06-secrets.md
@@@ -0,0 +1,374 @@
++# Secrets and auth mechanisms
++
++> Generated by `scripts/roadmap-baseline/generate.js`. Do not edit by hand; edit `data/*.json` or the extractor and regenerate.
++
++Deliverable 5. Names and locations only; no value was read. "In prod" names the `/etc/openvibe/*.env` file that sets the name.
++
++## Auth mechanisms
++
++| Mechanism | Used by | Where |
++|---|---|---|
++| User JWT (RS256, Network-issued, verified offline via JWKS) | all services | Network server/auth, CONTRACTS.md |
++| OAuth2 client credentials per app (OV_OAUTH_CLIENT_ID/SECRET) | Live, Tools, Games, Media, Community | CONTRACTS.md |
++| Shared internal key (X-Internal-Key / INTERNAL_API_KEY) | 5 calling repos | Network /internal/*, Live /internal/* |
++| Per-app Media API key (Bearer) | Live, Community, Tools | Media apps table + MEDIA_APP_KEYS |
++| HMAC webhook signatures | Media -> Live, PowerChat, Resend | X-OVMedia-Signature, Svix |
++| hbt_ API tokens | Live bots/integrations | Live api_tokens (docs/api-tokens.md) |
++| Stream keys | RTMP/WHIP publishers | Live managed_streams.stream_key (hazard H4) |
++
++## Secret-named variables
++
++| Repo | Variable | In prod | Read at |
++|---|---|---|---|
++| OpenVibe.Community | COOKIE_SECURE | **not set** | server/config.js |
++| OpenVibe.Community | OV_OAUTH_CLIENT_SECRET | community.env | server/config.js |
++| OpenVibe.Games | EDITOR_KEY | games.env | apps/server/src/config.ts |
++| OpenVibe.Games | OV_OAUTH_CLIENT_SECRET | games.env | apps/server/src/config.ts |
++| OpenVibe.Live | ADMIN_PASSWORD | live.env | server/config.js |
++| OpenVibe.Live | GH_TOKEN | **not set** | server/meta/routes.js |
++| OpenVibe.Live | GITHUB_TOKEN | **not set** | server/meta/routes.js |
++| OpenVibe.Live | INTERNAL_API_KEY | live.env | server/chat/chat-server.js, server/config.js, server/monetization/wallet-client.js +2 |
++| OpenVibe.Live | JWT_SECRET | live.env | server/arena/arena-service.js, server/config.js, server/integrations/platform-oauth.js +1 |
++| OpenVibe.Live | MEDIA_API_KEY | live.env | server/media-client.js |
++| OpenVibe.Live | MEDIA_WEBHOOK_SECRET | live.env | server/media-proxy/webhook.js |
++| OpenVibe.Live | OPS_ALERT_WEBHOOK_URL | **not set** | server/media-proxy/webhook.js |
++| OpenVibe.Live | OV_INTERNAL_KEY | **not set** | server/chat/chat-server.js, server/config.js, server/monetization/wallet-client.js +2 |
++| OpenVibe.Live | OV_OAUTH_CLIENT_SECRET | live.env | server/auth/routes.js |
++| OpenVibe.Live | PAYPAL_CLIENT_SECRET | live.env | server/config.js |
++| OpenVibe.Live | TURN_AUTH_SECRET | live.env | server/auth/routes.js, server/net/turn.js |
++| OpenVibe.Live | TURN_CREDENTIAL | live.env | server/config.js |
++| OpenVibe.Media | INTERNAL_API_KEY | media.env | server/config.js, server/views/service.js |
++| OpenVibe.Media | MEDIA_B2_APP_KEY | media.env | server/vod/vod-storage.js |
++| OpenVibe.Media | MEDIA_R2_SECRET_ACCESS_KEY | media.env | server/vod/vod-storage.js |
++| OpenVibe.Media | MEDIA_SECRET | **not set** | server/views/service.js |
++| OpenVibe.Media | OV_OAUTH_CLIENT_SECRET | media.env | server/index.js |
++| OpenVibe.Media | VIEW_HASH_SECRET | **not set** | server/views/service.js |
++| OpenVibe.Network | ADMIN_PASSWORD | network.env | server/config.js |
++| OpenVibe.Network | INTERNAL_API_KEY | network.env | server/admin/routes.js, server/config.js |
++| OpenVibe.Network | JWT_PRIVATE_KEY | network.env | server/config.js |
++| OpenVibe.Network | OV_GAMES_WEBHOOK_SECRET | **not set** | server/config.js |
++| OpenVibe.Network | OV_INTERNAL_KEY | **not set** | server/admin/routes.js |
++| OpenVibe.Network | OV_LIVE_WEBHOOK_SECRET | **not set** | server/config.js |
++| OpenVibe.Network | SETUP_TOKEN | **not set** | server/config.js |
++| OpenVibe.Tools | COOKIE_DOMAIN | **not set** | apps/gateway/server/config.js |
++| OpenVibe.Tools | COOKIE_SECURE | **not set** | apps/gateway/server/config.js |
++| OpenVibe.Tools | INTERNAL_API_KEY | tools.env | apps/_shared/internal-auth.js |
++| OpenVibe.Tools | NPS_API_KEY | **not set** | apps/maps/server/config.js |
++| OpenVibe.Tools | OPEN_CHARGE_MAP_KEY | **not set** | apps/maps/server/config.js |
++| OpenVibe.Tools | OV_INTERNAL_KEY | **not set** | apps/_shared/internal-auth.js |
++| OpenVibe.Tools | OV_OAUTH_CLIENT_SECRET | tools.env | apps/gateway/server/config.js |
++| OpenVibe.Tools | RIDB_API_KEY | **not set** | apps/maps/server/config.js, apps/maps/server/sources/ridb.js |
++
++## Env files on the production host
++
++| File | Variables |
++|---|---|
++| /etc/openvibe/community.env | 13 |
++| /etc/openvibe/games.env | 12 |
++| /etc/openvibe/generated-secrets.env | 6 |
++| /etc/openvibe/live.env | 53 |
++| /etc/openvibe/media.env | 26 |
++| /etc/openvibe/network.env | 21 |
++| /etc/openvibe/tools.env | 9 |
++
++## Other configuration variables
++
++| Repo | Variable | In prod | Read at |
++|---|---|---|---|
++| OpenVibe.Community | BASE_URL | community.env | server/config.js |
++| OpenVibe.Community | HOST | community.env | server/config.js |
++| OpenVibe.Community | LOGIN_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | NODE_ENV | community.env | server/config.js |
++| OpenVibe.Community | OV_AUDIO_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_COMMUNITY_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_DEV_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_DOCS_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_FOOD_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_GAMES_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_IMG_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_LIVE_INTERNAL_URL | community.env | server/config.js |
++| OpenVibe.Community | OV_LIVE_URL | community.env | server/config.js, vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_LOGO_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_MAPS_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_MEDIA_URL | community.env | server/config.js, vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_NET_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_NETWORK_INTERNAL_URL | community.env | server/config.js |
++| OpenVibe.Community | OV_NETWORK_LOGIN_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_NETWORK_URL | community.env | server/config.js, vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_OAUTH_CLIENT_ID | community.env | server/config.js |
++| OpenVibe.Community | OV_OAUTH_REDIRECT_URI | community.env | server/config.js |
++| OpenVibe.Community | OV_PASTES_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_TEXT_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_TOOLS_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | OV_YT_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Community | PORT | community.env | server/config.js |
++| OpenVibe.Community | TRUST_PROXY | community.env | server/config.js |
++| OpenVibe.Games | DB_PATH | games.env | apps/server/src/config.ts |
++| OpenVibe.Games | EVENT_INTERVAL_SCALE | | apps/server/src/config.ts |
++| OpenVibe.Games | GUEST_IP_BINDING | | apps/server/src/config.ts |
++| OpenVibe.Games | HOST | games.env | apps/server/src/config.ts |
++| OpenVibe.Games | LOG_LEVEL | | apps/server/src/main.ts |
++| OpenVibe.Games | MAP_PATH | | apps/server/src/config.ts |
++| OpenVibe.Games | NODE_ENV | games.env | (not referenced) |
++| OpenVibe.Games | OV_NETWORK_AUTH_URL | | apps/server/src/config.ts |
++| OpenVibe.Games | OV_NETWORK_URL | games.env | apps/server/src/config.ts |
++| OpenVibe.Games | OV_OAUTH_CLIENT_ID | games.env | apps/server/src/config.ts |
++| OpenVibe.Games | OWNER_USERNAME | games.env | apps/server/src/net/networkAuth.ts |
++| OpenVibe.Games | PLAY_URL | games.env | apps/server/src/config.ts |
++| OpenVibe.Games | PORT | games.env | (not referenced) |
++| OpenVibe.Games | SELF_URL | games.env | apps/server/src/config.ts |
++| OpenVibe.Games | STATIC_DIR | games.env | apps/server/src/config.ts |
++| OpenVibe.Live | ADMIN_USERNAME | live.env | server/config.js |
++| OpenVibe.Live | AI_HEAR_MAX_SPOOL | | server/ai/stream-audio.js |
++| OpenVibe.Live | AI_HEAR_SEGMENT_SEC | | server/ai/stream-audio.js |
++| OpenVibe.Live | AI_MOMENTS_PATH | | server/ai/stream-memory-job.js, server/arena/arena-service.js +1 |
++| OpenVibe.Live | AI_VOD_MAX_WINDOWS | | server/ai/media-analysis.js |
++| OpenVibe.Live | ALLOW_P2P_FALLBACK | | server/config.js |
++| OpenVibe.Live | ARENA_IMAGE_PATH | | server/arena/arena-service.js, server/index.js |
++| OpenVibe.Live | BASE_URL | live.env | server/auth/routes.js, server/config.js |
++| OpenVibe.Live | BTTV_CACHE_TTL | | server/config.js |
++| OpenVibe.Live | CLIPS_PATH | | server/config.js |
++| OpenVibe.Live | COLD_STORAGE_PATH | | server/config.js |
++| OpenVibe.Live | DB_PATH | live.env | server/config.js, server/db/database.js +1 |
++| OpenVibe.Live | DEBUG_DM_DELIVERY | | server/chat/chat-server.js |
++| OpenVibe.Live | EMOTES_PATH | live.env | server/config.js, server/media-proxy/asset-sync.js |
++| OpenVibe.Live | ESCROW_HOLD_DAYS | live.env | server/config.js |
++| OpenVibe.Live | FFZ_CACHE_TTL | | server/config.js |
++| OpenVibe.Live | HOST | live.env | server/config.js |
++| OpenVibe.Live | JSMPEG_AUDIO_PORT | live.env | server/config.js |
++| OpenVibe.Live | JSMPEG_VIDEO_PORT | live.env | server/config.js |
++| OpenVibe.Live | LISTEN_FDS | | server/index.js |
++| OpenVibe.Live | LISTEN_PID | | server/index.js |
++| OpenVibe.Live | LIVE_THUMBS_PATH | | server/media-proxy/live-thumbs.js |
++| OpenVibe.Live | LOGIN_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | MAX_EMOTE_SIZE_KB | live.env | server/config.js |
++| OpenVibe.Live | MAX_EMOTES_PER_CHANNEL | | server/config.js |
++| OpenVibe.Live | MAX_EMOTES_PER_UPLOADER_PER_CHANNEL | | server/config.js |
++| OpenVibe.Live | MAX_EMOTES_PER_USER | live.env | server/config.js |
++| OpenVibe.Live | MAX_SOUND_SIZE_KB | | server/config.js |
++| OpenVibe.Live | MAX_SOUNDS_PER_CHANNEL | | server/config.js |
++| OpenVibe.Live | MAX_SOUNDS_PER_UPLOADER_PER_CHANNEL | | server/config.js |
++| OpenVibe.Live | MAX_VOD_SIZE_MB | | server/config.js, server/media-proxy/vods.js |
++| OpenVibe.Live | MEDIA_APP_ID | live.env | server/config.js, server/media-client.js |
++| OpenVibe.Live | MEDIA_PUBLIC_URL | live.env | server/config.js, server/media-client.js |
++| OpenVibe.Live | MEDIA_URL | live.env | server/config.js, server/media-client.js |
++| OpenVibe.Live | MEDIASOUP_ANNOUNCED_IP | live.env | server/config.js |
++| OpenVibe.Live | MEDIASOUP_LISTEN_IP | live.env | server/config.js |
++| OpenVibe.Live | MEDIASOUP_MAX_PORT | live.env | server/config.js |
++| OpenVibe.Live | MEDIASOUP_MIN_PORT | live.env | server/config.js |
++| OpenVibe.Live | MIN_CASHOUT | live.env | (not referenced) |
++| OpenVibe.Live | MIN_CASHOUT_BUCKS | | server/config.js |
++| OpenVibe.Live | NODE_ENV | live.env | server/config.js, server/index.js |
++| OpenVibe.Live | OFFLINE_SCREEN_PATH | | server/monetization/routes.js, server/streaming/routes.js |
++| OpenVibe.Live | OV_APP_ROOT | | server/docs/routes.js, server/web/assets.js |
++| OpenVibe.Live | OV_AUDIO_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_COMMUNITY_URL | live.env | server/index.js, server/seo/seo.js +1 |
++| OpenVibe.Live | OV_DEV_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_DOCS_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_FOOD_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_GAMES_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_IMG_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_LIVE_URL | live.env | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_LOGO_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_MAPS_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_MEDIA_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_NET_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_NETWORK_INTERNAL_URL | live.env | server/chat/chat-server.js, server/monetization/wallet-client.js +1 |
++| OpenVibe.Live | OV_NETWORK_LOGIN_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_NETWORK_PUBLIC_KEY | live.env | server/auth/auth.js |
++| OpenVibe.Live | OV_NETWORK_URL | live.env | server/auth/auth.js, server/config.js +2 |
++| OpenVibe.Live | OV_OAUTH_CLIENT_ID | live.env | server/auth/routes.js |
++| OpenVibe.Live | OV_PASTES_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_PUBLIC_DIR | | server/web/assets.js |
++| OpenVibe.Live | OV_TEXT_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_TOOLS_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OV_YT_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Live | OWNER_USERNAME | | server/db/database.js |
++| OpenVibe.Live | PASTES_ON_COMMUNITY | live.env | server/index.js, server/seo/seo.js |
++| OpenVibe.Live | PATH | | server/media/media-downloader.js |
++| OpenVibe.Live | PAYPAL_CLIENT_ID | live.env | server/config.js |
++| OpenVibe.Live | PAYPAL_MODE | live.env | server/config.js |
++| OpenVibe.Live | PORT | live.env | server/config.js |
++| OpenVibe.Live | RS_COMPANION_DB_PATH | live.env | (not referenced) |
++| OpenVibe.Live | RS_PASSTHROUGH | | server/config.js |
++| OpenVibe.Live | RS_PASSTHROUGH_INJECT_PARAMS | | server/integrations/rs-passthrough-relay.js |
++| OpenVibe.Live | RS_PASSTHROUGH_ROBOTS | live.env | (not referenced) |
++| OpenVibe.Live | RTMP_CHUNK_SIZE | live.env | server/config.js |
++| OpenVibe.Live | RTMP_HOST | live.env | server/config.js |
++| OpenVibe.Live | RTMP_PORT | live.env | server/config.js |
++| OpenVibe.Live | SEVENTV_CACHE_TTL | | server/config.js |
++| OpenVibe.Live | SITE_URL | | server/chat/chat-server.js |
++| OpenVibe.Live | SOUND_DEFAULT_MAX_SECONDS | | server/config.js |
++| OpenVibe.Live | SOUNDS_PATH | | server/config.js, server/media-proxy/asset-sync.js |
++| OpenVibe.Live | STAR_STREAMER | live.env | server/config.js |
++| OpenVibe.Live | THUMBNAILS_PATH | | server/config.js |
++| OpenVibe.Live | TTS_CACHE_PATH | | server/arena/voice.js |
++| OpenVibe.Live | TURN_URL | live.env | server/config.js |
++| OpenVibe.Live | TURN_USERNAME | live.env | server/config.js |
++| OpenVibe.Live | UNSAFE_WORDS_PATH | live.env | (not referenced) |
++| OpenVibe.Live | VOD_PATH | | server/config.js |
++| OpenVibe.Live | WEBRTC_PORT | live.env | server/config.js |
++| OpenVibe.Live | WHIP_PUBLIC_URL | | server/config.js |
++| OpenVibe.Live | WHIP_PUBLIC_URL_ENABLED | | server/config.js |
++| OpenVibe.Live | WHISPER_BEAM | | server/ai/transcribe.js |
++| OpenVibe.Live | WHISPER_BIN | live.env | server/ai/transcribe.js |
++| OpenVibe.Live | WHISPER_MAX_CONCURRENT | | server/ai/transcribe.js |
++| OpenVibe.Live | WHISPER_MODEL | live.env | server/ai/transcribe.js |
++| OpenVibe.Live | WHISPER_MODEL_LIVE | live.env | server/ai/transcribe.js |
++| OpenVibe.Live | WHISPER_MODEL_MULTI | live.env | server/ai/transcribe.js |
++| OpenVibe.Live | WHISPER_THREADS | | server/ai/transcribe.js |
++| OpenVibe.Live | WHISPER_VAD | | server/ai/transcribe.js |
++| OpenVibe.Live | WHISPER_VAD_MODEL | live.env | server/ai/transcribe.js |
++| OpenVibe.Live | YAMNET_CLASSMAP | | server/ai/audio-events.js |
++| OpenVibe.Live | YAMNET_MIN_CONFIDENCE | | server/ai/audio-events.js |
++| OpenVibe.Live | YAMNET_MODEL | | server/ai/audio-events.js |
++| OpenVibe.Live | YT_DAILY_QUOTA | | server/integrations/platform-oauth.js |
++| OpenVibe.Live | YT_MIN_CREATE_INTERVAL_MS | | server/integrations/platform-oauth.js |
++| OpenVibe.Live | YT_QUOTA_COOLDOWN_MS | | server/integrations/platform-oauth.js |
++| OpenVibe.Live | YT_QUOTA_HARD | | server/integrations/platform-oauth.js |
++| OpenVibe.Live | YT_QUOTA_SOFT | | server/integrations/platform-oauth.js |
++| OpenVibe.Live | YTDLP_NODE_PATH | | server/media/media-downloader.js |
++| OpenVibe.Live | YTDLP_PATH | | server/media/media-downloader.js |
++| OpenVibe.Media | APP_INTERNAL_URLS | | server/thumbnails/live-frame-service.js |
++| OpenVibe.Media | ASSETS_PATH | | server/config.js |
++| OpenVibe.Media | CLIP_MAX_CONCURRENT_FFMPEG | | server/vod/clip-cutter.js |
++| OpenVibe.Media | CLIP_QUEUE_WAIT_MS | | server/vod/clip-cutter.js |
++| OpenVibe.Media | CLIPS_PATH | media.env | server/config.js |
++| OpenVibe.Media | DB_PATH | media.env | server/config.js |
++| OpenVibe.Media | FILES_PATH | media.env | server/config.js |
++| OpenVibe.Media | HOST | media.env | server/config.js |
++| OpenVibe.Media | LIVE_APP_INTERNAL_URL | | server/thumbnails/live-frame-service.js |
++| OpenVibe.Media | LOGIN_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | MEDIA_APP_KEYS | | server/config.js |
++| OpenVibe.Media | MEDIA_APPS_SEED | media.env | server/config.js |
++| OpenVibe.Media | MEDIA_B2_BUCKET | media.env | server/vod/vod-storage.js |
++| OpenVibe.Media | MEDIA_B2_ENDPOINT | media.env | server/vod/vod-storage.js |
++| OpenVibe.Media | MEDIA_B2_KEY_ID | media.env | server/vod/vod-storage.js |
++| OpenVibe.Media | MEDIA_B2_REGION | media.env | server/vod/vod-storage.js |
++| OpenVibe.Media | MEDIA_PUBLIC_URL | | server/config.js |
++| OpenVibe.Media | MEDIA_R2_ACCESS_KEY_ID | media.env | server/vod/vod-storage.js |
++| OpenVibe.Media | MEDIA_R2_BUCKET | media.env | server/vod/vod-storage.js |
++| OpenVibe.Media | MEDIA_R2_ENDPOINT | media.env | server/vod/vod-storage.js |
++| OpenVibe.Media | MEDIA_R2_REGION | | server/vod/vod-storage.js |
++| OpenVibe.Media | MIN_VOD_SECONDS | | server/vod/finalize.js |
++| OpenVibe.Media | NODE_ENV | media.env | server/config.js |
++| OpenVibe.Media | OV_AUDIO_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_COMMUNITY_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_DEV_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_DOCS_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_FOOD_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_GAMES_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_IMG_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_LIVE_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_LOGO_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_MAPS_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_MEDIA_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_NET_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_NETWORK_INTERNAL_URL | media.env | server/config.js |
++| OpenVibe.Media | OV_NETWORK_LOGIN_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_NETWORK_URL | media.env | server/config.js, vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_OAUTH_CLIENT_ID | media.env | server/index.js |
++| OpenVibe.Media | OV_OAUTH_REDIRECT_URI | | server/index.js |
++| OpenVibe.Media | OV_PASTES_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_TEXT_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_TOOLS_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | OV_YT_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Media | PASTES_PATH | media.env | server/config.js |
++| OpenVibe.Media | PORT | media.env | (not referenced) |
++| OpenVibe.Media | RTP_PORT_MAX | media.env | (not referenced) |
++| OpenVibe.Media | RTP_PORT_MIN | media.env | (not referenced) |
++| OpenVibe.Media | THUMBNAILS_PATH | media.env | server/config.js |
++| OpenVibe.Media | VOD_DISK_CRIT_GB | | server/vod/recorder.js |
++| OpenVibe.Media | VOD_DISK_WARN_GB | | server/vod/recorder.js |
++| OpenVibe.Media | VOD_NO_NORMALIZE | | server/vod/recorder.js |
++| OpenVibe.Media | VOD_PATH | media.env | server/config.js |
++| OpenVibe.Media | VOD_STOP_GRACE_MS | | server/vod/recorder.js |
++| OpenVibe.Network | ADMIN_USERNAME | network.env | server/config.js |
++| OpenVibe.Network | AVATAR_PATH | | server/config.js |
++| OpenVibe.Network | BASE_URL | network.env | server/admin/routes.js, server/config.js +1 |
++| OpenVibe.Network | BOOTSTRAP_PROFILE | | server/config.js, server/db/database.js |
++| OpenVibe.Network | CERTBOT_BIN | | server/deploy/cert-manager.js |
++| OpenVibe.Network | DB_PATH | network.env | server/config.js, server/grant-admin.js +1 |
++| OpenVibe.Network | HOST | network.env | server/config.js |
++| OpenVibe.Network | INTERNAL_URL | | server/config.js |
++| OpenVibe.Network | JWT_PUBLIC_KEY | network.env | server/config.js |
++| OpenVibe.Network | LETSENCRYPT_DIR | | server/deploy/cert-manager.js |
++| OpenVibe.Network | LOGIN_URL | | packages/openvibe-shared/brand.js, server/config.js |
++| OpenVibe.Network | MEDIASOUP_ANNOUNCED_IP | | server/domains/dns-check.js |
++| OpenVibe.Network | NODE_ENV | network.env | server/auth/fedcm.js, server/auth/sso-check.js +3 |
++| OpenVibe.Network | NOTIFICATION_EMAIL_CRITICAL_ONLY | network.env | (not referenced) |
++| OpenVibe.Network | NOTIFICATION_MAX_AGE_DAYS | network.env | (not referenced) |
++| OpenVibe.Network | NOTIFICATIONS_ENABLED | network.env | (not referenced) |
++| OpenVibe.Network | OPENVIBELIVE_DB_PATH | | server/db/database.js |
++| OpenVibe.Network | OV_AUDIO_URL | | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_COMMUNITY_URL | | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_DEV_URL | | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_DOCS_URL | | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_FOOD_URL | | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_GAMES_INTERNAL_URL | network.env | server/config.js |
++| OpenVibe.Network | OV_GAMES_URL | network.env | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_IMG_URL | | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_LIVE_INTERNAL_URL | network.env | server/admin/routes.js, server/config.js |
++| OpenVibe.Network | OV_LIVE_URL | network.env | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_LOGO_URL | | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_MAPS_URL | | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_MEDIA_INTERNAL_URL | network.env | server/config.js |
++| OpenVibe.Network | OV_MEDIA_URL | network.env | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_NET_URL | | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_NETWORK_BASE_URL | | server/auth/reset-tokens.js |
++| OpenVibe.Network | OV_NETWORK_INTERNAL_URL | | server/config.js |
++| OpenVibe.Network | OV_NETWORK_LOGIN_URL | | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_NETWORK_URL | | packages/openvibe-shared/brand.js, server/config.js |
++| OpenVibe.Network | OV_PASTES_URL | | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_SSO_TARGETS | | server/auth/sso-targets.js |
++| OpenVibe.Network | OV_SSO_TARGETS_DISABLED | | server/auth/sso-targets.js |
++| OpenVibe.Network | OV_TEXT_URL | | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_TOOLS_INTERNAL_URL | network.env | server/config.js, server/domains/catalog.js |
++| OpenVibe.Network | OV_TOOLS_URL | network.env | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OV_YT_URL | | packages/openvibe-shared/brand.js |
++| OpenVibe.Network | OWNER_USERNAME | | server/admin/routes.js, server/auth/owner-guard.js +3 |
++| OpenVibe.Network | PORT | network.env | server/config.js |
++| OpenVibe.Network | PUBLIC_IP | | server/domains/dns-check.js |
++| OpenVibe.Network | SSH_PROJECTS_ROOT | | server/index.js |
++| OpenVibe.Network | SSH_SERVER_HOST | | server/index.js |
++| OpenVibe.Tools | BASE_URL | | apps/gateway/server/config.js, apps/maps/server/config.js +1 |
++| OpenVibe.Tools | DATA_DIR | | apps/audio/server/config.js, apps/docs/server/config.js +2 |
++| OpenVibe.Tools | DOWNLOADS_DIR | | apps/yt/server/config.js |
++| OpenVibe.Tools | HOST | tools.env | apps/audio/server/config.js, apps/docs/server/config.js +5 |
++| OpenVibe.Tools | LIVE_URL | | apps/gateway/server/config.js |
++| OpenVibe.Tools | LOGIN_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | MAPS_API | | apps/food/server/index.js |
++| OpenVibe.Tools | MEDIA_PUBLIC_URL | tools.env | (not referenced) |
++| OpenVibe.Tools | MEDIA_URL | tools.env | (not referenced) |
++| OpenVibe.Tools | NODE_ENV | tools.env | apps/audio/server/index.js, apps/docs/server/index.js +6 |
++| OpenVibe.Tools | OUTPUT_DIR | | apps/audio/server/config.js, apps/docs/server/config.js +1 |
++| OpenVibe.Tools | OV_AUDIO_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_COMMUNITY_URL | | apps/gateway/server/index.js, vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_DEV_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_DOCS_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_DOMAINS_URL | | apps/gateway/server/registry/index.js |
++| OpenVibe.Tools | OV_ENFORCE_HOSTS | | apps/gateway/server/index.js |
++| OpenVibe.Tools | OV_EXTRA_HOSTS | | apps/_shared/host-role.js |
++| OpenVibe.Tools | OV_FOOD_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_GAMES_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_IMG_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_LIVE_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_LOGO_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_MAPS_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_MEDIA_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_NET_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_NETWORK_INTERNAL_URL | tools.env | apps/audio/server/config.js, apps/docs/server/config.js +3 |
++| OpenVibe.Tools | OV_NETWORK_LOGIN_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_NETWORK_PUBLIC_KEY | | apps/audio/server/config.js, apps/docs/server/config.js +2 |
++| OpenVibe.Tools | OV_NETWORK_URL | tools.env | apps/audio/server/config.js, apps/docs/server/config.js +5 |
++| OpenVibe.Tools | OV_OAUTH_CLIENT_ID | tools.env | apps/gateway/server/config.js |
++| OpenVibe.Tools | OV_OAUTH_REDIRECT_URI | | apps/gateway/server/config.js |
++| OpenVibe.Tools | OV_PASTES_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_TEXT_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_TOOLS_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | OV_YT_URL | | vendor/openvibe-shared/brand.js |
++| OpenVibe.Tools | PORT | | apps/audio/server/config.js, apps/docs/server/config.js +6 |
++| OpenVibe.Tools | UPLOADS_DIR | | apps/audio/server/config.js, apps/docs/server/config.js +1 |
++| OpenVibe.Tools | YT_COOKIES_FILE | | apps/yt/server/downloader.js |
++| OpenVibe.Tools | YT_PROXY | | apps/yt/server/downloader.js |
++| OpenVibe.Tools | YTDLP_PATH | | apps/yt/server/config.js |
ddiff --git a/docs/roadmap-baseline/07-ownership.md b/docs/roadmap-baseline/07-ownership.md
nnew file mode 100644
iindex 00000000..c1b701e8
---- /dev/null
++++ b/docs/roadmap-baseline/07-ownership.md
@@@ -0,0 +1,45 @@
++# Data ownership: current vs target
++
++> Generated by `scripts/roadmap-baseline/generate.js`. Do not edit by hand; edit `data/*.json` or the extractor and regenerate.
++
++Deliverable 6. Current owner = the repo that creates the table; target owner = roadmap sections 14.1 and 16, applied by `data/ownership-rules.json`.
++
++## Summary
++
++| Current owner | Tables | Stay | Move | Frozen legacy | Investigate |
++|---|---|---|---|---|---|
++| OpenVibe.Live | 186 | 26 | 94 | 43 | 23 |
++| OpenVibe.Network | 35 | 32 | 3 | 0 | 0 |
++| OpenVibe.Media | 15 | 12 | 3 | 0 | 0 |
++| OpenVibe.Tools | 4 | 4 | 0 | 0 | 0 |
++| OpenVibe.Community | 4 | 4 | 0 | 0 | 0 |
++| OpenVibe.Games | 5 | 5 | 0 | 0 | 0 |
++| OpenVibe.Sites | 0 | 0 | 0 | 0 | 0 |
++
++## Authority moves
++
++| Target authority | From | Tables | Wave |
++|---|---|---|---|
++| archive then drop | OpenVibe.Live | arena_achievements, arena_battles, arena_events, 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_viewer_clout, arena_votes, chatter_profiles, chatter_subjects, chatter_xp_log, promo_claims | W0 |
++| OpenRe.Stream | OpenVibe.Live | camera_presets, camera_profiles, cameras, control_config_buttons, control_configs, control_whitelist, kick_channel_cache, managed_streams, platform_connections, restream_destinations, robotstreamer_integrations, robotstreamer_integrations_new, stream_controls, stream_controls_new | W7 |
++| OpenVibe.AI | OpenVibe.Live | ai_chatbot_configs, ai_timeline_cache, ai_usage, ai_viewer_log, ai_viewer_threads, channel_ai_bots, channel_ai_config, clip_ai_state, easter_egg_solves, stream_memories, stream_recaps, streamer_overviews, translations, vod_ai_state | W13 |
++| OpenVibe.Billing | OpenVibe.Live | payment_orders, transactions | W8 (after ADR-012) |
++| OpenVibe.Billing (loyalty ledger) | OpenVibe.Live | channel_points, coin_redemptions, coin_rewards, coin_transactions, user_cosmetics, user_equipped, user_equipped_tag | W8 (after ADR-012) |
++| OpenVibe.Billing (loyalty ledger) | OpenVibe.Network | coin_transactions, wallets | W8 (after ADR-012) |
++| OpenVibe.Billing + OpenVibe.VIP | OpenVibe.Live | subscriptions | W8/W10 |
++| OpenVibe.Chat | OpenVibe.Live | channel_moderation_settings, channel_moderators, channel_sounds, channel_sounds_new, chat_ai_summaries, chat_messages, chat_messages_new, chat_timeline_events, dm_blocks, dm_conversations, dm_messages, dm_participants, emotes, emotes_new, hidden_relay_users, media_request_settings, media_requests, moderation_actions, pending_ip_messages, relay_users, stream_first_chats, tts_voice_overrides, user_tags | W6 |
++| OpenVibe.Codes | OpenVibe.Live | vibe_coding_events, vibe_coding_sessions | W20 |
++| OpenVibe.Community | OpenVibe.Live | comments, paste_comments, paste_likes, pastes | W5 |
++| OpenVibe.Community | OpenVibe.Media | paste_comments, paste_likes, pastes | W5 |
++| OpenVibe.Events | OpenVibe.Network | stream_live_announcements | W3 |
++| OpenVibe.Games | OpenVibe.Live | canvas_actions, canvas_bans, canvas_region_locks, canvas_settings, canvas_snapshots, canvas_tiles, canvas_user_overrides, 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 | W12 |
++| OpenVibe.Games (import source) | OpenVibe.Live | 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, mine_profile, mod_persuasion, music_profile, roast_dossier, roast_log, unlocked_recipes, user_voice_selection, user_voices, woodcut_profile | W12 |
++| OpenVibe.Media | OpenVibe.Live | clips, vods | W4 |
++| OpenVibe.Network | OpenVibe.Live | follows, linked_accounts, themes, user_preferences, user_themes, users, verification_keys | W1 |
++| OpenVibe.Network (policy) | OpenVibe.Live | anon_ip_mappings, approved_ips, bans, ip_log, vpn_approvals | W1 |
++| OpenVibe.Network (principals) / OpenVibe.Codes | OpenVibe.Live | api_keys, api_tokens | W1/W20 |
++| OpenVibe.Tips | OpenVibe.Live | donation_goals | W9 |
++
++## Unclassified
++
++None.
ddiff --git a/docs/roadmap-baseline/08-discrepancies.md b/docs/roadmap-baseline/08-discrepancies.md
nnew file mode 100644
iindex 00000000..17b3d216
---- /dev/null
++++ b/docs/roadmap-baseline/08-discrepancies.md
@@@ -0,0 +1,116 @@
++# Production vs repository
++
++> Generated by `scripts/roadmap-baseline/generate.js`. Do not edit by hand; edit `data/*.json` or the extractor and regenerate.
++
++Deliverable 7. Snapshot of openvibe-oregon taken 2026-09-22 20:43 UTC compared with the local checkouts. `unknown` rows are things this environment cannot see.
++
++| Severity | Area | Subject | Detail |
++|---|---|---|---|
++| low | database | /opt/openvibe.live/data/backups/live-pre-643a2e6.db | 264.7 MB backup inside the service data dir |
++| low | database | /opt/openvibe.live/data/hobo.db | 0 B, zero tables (dead file) |
++| low | database | /opt/openvibe.live/data/openvibe.db | 0 B, zero tables (dead file) |
++| low | deploy | games | deployed 43c11f0 has the same file tree as origin/main 5710287 but a different SHA (pre-rewrite history); reset the checkout so later pulls fast-forward |
++| low | env | OpenVibe.Community:COOKIE_SECURE | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Live:GH_TOKEN | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Live:GITHUB_TOKEN | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Live:OPS_ALERT_WEBHOOK_URL | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Live:OV_INTERNAL_KEY | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Media:MEDIA_SECRET | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Media:VIEW_HASH_SECRET | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Network:OV_GAMES_WEBHOOK_SECRET | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Network:OV_INTERNAL_KEY | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Network:OV_LIVE_WEBHOOK_SECRET | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Network:SETUP_TOKEN | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Tools:COOKIE_DOMAIN | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Tools:COOKIE_SECURE | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Tools:NPS_API_KEY | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Tools:OPEN_CHARGE_MAP_KEY | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Tools:OV_INTERNAL_KEY | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | env | OpenVibe.Tools:RIDB_API_KEY | secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback |
++| low | schema | OpenVibe.Live:active_effects | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:ai_conversations | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:ai_memory | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:analytics_snapshots | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:arena_achievements | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_battles | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_events | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_talk | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_talk_hype | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_talk_session_hype | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_talk_session_topics | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_talk_sessions | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_talk_topics | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_tier_paid | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_topic_hype | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_topic_members | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_topic_moments | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_topic_progress | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_topic_sides | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_topic_threads | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_topics | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_viewer_clout | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:arena_votes | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:bank_items | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:battle_stats | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:canvas_actions | in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games |
++| low | schema | OpenVibe.Live:canvas_bans | in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games |
++| low | schema | OpenVibe.Live:canvas_region_locks | in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games |
++| low | schema | OpenVibe.Live:canvas_settings | in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games |
++| low | schema | OpenVibe.Live:canvas_snapshots | in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games |
++| low | schema | OpenVibe.Live:canvas_tiles | in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games |
++| low | schema | OpenVibe.Live:canvas_user_overrides | in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games |
++| low | schema | OpenVibe.Live:changelog_entries | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:chat_log | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:chatter_profiles | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:chatter_subjects | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:chatter_xp_log | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:custom_rewards | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:daily_claims | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:divorce_history | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:dungeon_stats | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:event_log | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:farm_plots | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:file_checksums | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:fish_profile | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:gusher_profile | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:inventory | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:jo_profile | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:love_affinity | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:love_profile | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:love_proposals | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:mine_profile | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:mod_persuasion | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:music_profile | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:promo_claims | in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop |
++| low | schema | OpenVibe.Live:roast_dossier | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:roast_log | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:unlocked_recipes | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:user_voice_selection | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:user_voices | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| low | schema | OpenVibe.Live:woodcut_profile | in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source) |
++| info | env | OpenVibe.Games:NODE_ENV | set in games.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | env | OpenVibe.Games:PORT | set in games.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | env | OpenVibe.Live:MIN_CASHOUT | set in live.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | env | OpenVibe.Live:RS_COMPANION_DB_PATH | set in live.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | env | OpenVibe.Live:RS_PASSTHROUGH_ROBOTS | set in live.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | env | OpenVibe.Live:UNSAFE_WORDS_PATH | set in live.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | env | OpenVibe.Media:PORT | set in media.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | env | OpenVibe.Media:RTP_PORT_MAX | set in media.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | env | OpenVibe.Media:RTP_PORT_MIN | set in media.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | env | OpenVibe.Network:NOTIFICATION_EMAIL_CRITICAL_ONLY | set in network.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | env | OpenVibe.Network:NOTIFICATION_MAX_AGE_DAYS | set in network.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | env | OpenVibe.Network:NOTIFICATIONS_ENABLED | set in network.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | env | OpenVibe.Tools:MEDIA_PUBLIC_URL | set in tools.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | env | OpenVibe.Tools:MEDIA_URL | set in tools.env but not referenced by scanned code (may be read by a dependency or stale) |
++| info | schema | OpenVibe.Live:channel_sounds_new | declared in code but absent from every production database for this service (lazy/unused/legacy migration) |
++| info | schema | OpenVibe.Live:chat_messages_new | declared in code but absent from every production database for this service (lazy/unused/legacy migration) |
++| info | schema | OpenVibe.Live:emotes_new | declared in code but absent from every production database for this service (lazy/unused/legacy migration) |
++| info | schema | OpenVibe.Live:robotstreamer_integrations_new | declared in code but absent from every production database for this service (lazy/unused/legacy migration) |
++| info | schema | OpenVibe.Live:stream_controls_new | declared in code but absent from every production database for this service (lazy/unused/legacy migration) |
++| info | schema | OpenVibe.Media:analytics_daily | declared in code but absent from every production database for this service (lazy/unused/legacy migration) |
++| info | schema | OpenVibe.Media:analytics_events | declared in code but absent from every production database for this service (lazy/unused/legacy migration) |
++| info | schema | OpenVibe.Media:analytics_hourly | declared in code but absent from every production database for this service (lazy/unused/legacy migration) |
++| info | schema | OpenVibe.Media:analytics_rate_tracking | declared in code but absent from every production database for this service (lazy/unused/legacy migration) |
++| info | schema | OpenVibe.Network:admin_rate_limits | declared in code but absent from every production database for this service (lazy/unused/legacy migration) |
++| unknown | host | powerchat.gg, raspi/hobo.tools (LAN) | unreachable from this environment; state not verified |
++| unknown | provider | B2/R2/Cloudflare/PayPal consoles | console configuration (billing, lifecycle rules, zone settings, app config) is not visible from this environment |
ddiff --git a/docs/roadmap-baseline/09-d-status.md b/docs/roadmap-baseline/09-d-status.md
nnew file mode 100644
iindex 00000000..d7d0b50a
---- /dev/null
++++ b/docs/roadmap-baseline/09-d-status.md
@@@ -0,0 +1,57 @@
++# D01-D46 status
++
++> Generated by `scripts/roadmap-baseline/generate.js`. Do not edit by hand; edit `data/*.json` or the extractor and regenerate.
++
++Deliverable 8. Family-level status from roadmap 3.2. Evidence paths are checked on disk and metrics are recomputed from this scan each run; a family whose evidence does not resolve is marked unverified.
++
++| IDs | Family | Status | Disposition | Wave | Evidence | Measured |
++|---|---|---|---|---|---|---|
++| D01 | Identity/account | partial | partial | W1 | Network/server/auth; Community/server/live-client.js | 7 Live tables target OpenVibe.Network |
++| D02-D04 | Registry, capabilities, contracts | absent | missing | W1, W2 | Network/server/url-registry.js; Sites/sites.json | 21 charter-only repos of 32; Live 15, Network 0, Media 0, Tools 0, Community 0, Games 0, Sites 0 |
++| D05-D06 | Policy, staff, user modules | partial | partial | W1, W2 | Network/server/admin; Live/server/auth/permissions.js | - |
++| D07-D10 | Themes, notifications, social, analytics | partial | partial | W1, W2, Track O | Network/server/themes; Network/server/notifications | 5 repos carry their own analytics_events |
++| D11-D13 | Events, realtime, SDK | absent | missing | W2, W3 | Media/server/webhooks.js | 65 caller-file/target pairs across 50 files; 10 WebSocket server constructions |
++| D14-D16 | Media | partial | partial | W4 | Media/server/vod; Media/server/pastes | 3 Media tables target OpenVibe.Community |
++| D17-D20 | Live, OpenRe, channel/VOD lineage | partial | partial | W7 | Live/server/streaming/rtmp-server.js; Live/server/streaming/restream-manager.js | 14 Live tables target OpenRe.Stream |
++| D21-D23 | Chat | partial | partial | W6 | Live/server/chat/chat-server.js; Live/server/streaming/call-server.js | 23 Live tables target OpenVibe.Chat |
++| D24-D27 | Community | partial | partial | W5 | Community/server/pastes | 4 tables; 4 Live tables target OpenVibe.Community |
++| D28-D30 | Billing, Tips, VIP | partial | partial | W8-W10 (ADR-012 first) | Live/server/monetization/payments.js; Network/server/coins | 2 Live tables target OpenVibe.Billing; 2 Network tables target OpenVibe.Billing (loyalty ledger) |
++| D31 | Tools | built | already-present-and-verified | W11 | Tools/apps/gateway | 2 test files |
++| D32 | Games | built | already-present-and-verified | W12 | Games/apps/server/src/net/sso.ts | 56 test files |
++| D33-D34 | Codes, Host | absent | missing | W20, W21 | - | 21 charter-only repos of 32 |
++| D35-D38 | AI, content, sources, search | partial | partial | W13-W15 | Live/server/ai/moment-registry.js; Live/server/ai/llm.js | 14 Live tables target OpenVibe.AI |
++| D39 | Mods | absent | missing | W12, W21 | - | - |
++| D40 | Open developer services | absent | missing | W20, W21 | Live/server/integrations/powerchat-routes.js | 28 of 65 call sites authenticate with the shared internal key |
++| D41 | Independent runtime lifecycle | partial | partial | W7, W21 | Live/deploy | hazard H1; hazard H3 |
++| D42 | Shared component architecture | partial | partial | W2, Track R | Network/packages/openvibe-shared | 5 repos vendor openvibe-shared (129 files); 4 divergent: Community/ov-icons.js, Live/user-card.js, Media/ov-icons.js, Tools/ov-icons.js |
++| D43 | Safe active-client updates | absent | missing | Track R | Live/server/chat/deploy-notice.js; Live/server/web/assets.js | - |
++| D44 | Crawlable component rendering | built | already-present-and-verified | Track R (preserve) | Community/server/seo.js; Sites/build.js | - |
++| D45 | Release compatibility and recovery | absent | missing | Track R, W22 | - | 1 service(s) not running origin/main |
++| D46 | Update observability and acceptance | absent | missing | Track R, Track O | - | 0 of 7 scanned repos have .github/workflows |
++
++Totals: 3 built, 11 partial, 8 absent across 22 families; 22 verified.
++
++## Findings
++
++- **D01 Identity/account.** Network is a real OAuth2/OIDC IdP; Live keeps its own users table and Community translates identity through Live.
++- **D02-D04 Registry, capabilities, contracts.** Three partial registries (url_registry, Tools catalog, Sites catalog); contracts are prose in CONTRACTS.md; no capability layer.
++- **D05-D06 Policy, staff, user modules.** Roles and admin exist per service; no shared capability map; versioned user modules do not exist.
++- **D07-D10 Themes, notifications, social, analytics.** Themes and notifications are shared and real; follows exist in both Live and Network; analytics is per site with no retention.
++- **D11-D13 Events, realtime, SDK.** Cross-service effects are direct internal POSTs and HMAC webhooks; every realtime channel is a product-local WebSocket server; no SDK.
++- **D14-D16 Media.** Real service with B2/R2 tiering; predecessor-shaped vods/clips/files tables, still owns paste semantics.
++- **D17-D20 Live, OpenRe, channel/VOD lineage.** Live owns RTMP/WHIP/WebRTC ingest and restream in-process.
++- **D21-D23 Chat.** Rooms, DMs, TTS, soundboard, calls and moderation work, all as a Live module.
++- **D24-D27 Community.** SSR paste pages only; no database; comments live in Live, paste semantics in Media and (frozen) Live.
++- **D28-D30 Billing, Tips, VIP.** Money split across Network (OpenCoins wallet) and Live (Vibes/PayPal, subscriptions); no ledger authority or reconciliation.
++- **D31 Tools.** Gateway plus 7 satellites running; shared jobs/media primitives absent.
++- **D32 Games.** Authoritative world with Network SSO; not on shared chrome, no mod platform.
++- **D33-D34 Codes, Host.** Charter repositories only.
++- **D35-D38 AI, content, sources, search.** Large AI subsystem inside Live; no source registry, search index or publishing runtime.
++- **D39 Mods.** No mod manifest, grants or sandbox in the platform (Source.OpenVibe.Games has its own runtime, not audited here).
++- **D40 Open developer services.** Only a first-party PowerChat integration; internal calls use shared loopback keys.
++- **D41 Independent runtime lifecycle.** Repos deploy independently and Live is socket-activated, but Live restarts drop RTMP and Media restarts orphan recordings.
++- **D42 Shared component architecture.** openvibe-shared is vendored by copy; consumers can drift from canonical.
++- **D43 Safe active-client updates.** Live has a deploy notice and content-hashed assets; no release manifests or update coordinator.
++- **D44 Crawlable component rendering.** SSR/static with metadata in Network, Tools, Media, Community and Sites.
++- **D45 Release compatibility and recovery.** Deploy is git pull + restart; no versioned releases or mixed-version policy.
++- **D46 Update observability and acceptance.** No CI outside AFResume; no release telemetry.
ddiff --git a/docs/roadmap-baseline/10-hazards.md b/docs/roadmap-baseline/10-hazards.md
nnew file mode 100644
iindex 00000000..f209c6a8
---- /dev/null
++++ b/docs/roadmap-baseline/10-hazards.md
@@@ -0,0 +1,39 @@
++# Hazard register
++
++> Generated by `scripts/roadmap-baseline/generate.js`. Do not edit by hand; edit `data/*.json` or the extractor and regenerate.
++
++Deliverable 9. **Not yet reviewed by the production-host owner** (Wave 0 exit criterion EC7). "Check" is re-evaluated on every run.
++
++| ID | Hazard | Severity | Status | Owner | Wave | Mitigation | Check |
++|---|---|---|---|---|---|---|---|
++| H1 | Restarting Live drops active RTMP streams | high | open | Live maintainers | W7 (OpenRe extraction removes the class); W21 encodes the rule in deploy tooling | Deploy Live with deploy.sh --wait-idle after checking /api/streams; never restart mid-broadcast for UI-only changes (static deploys need no restart). | - |
++| H2 | Stopping the Live socket unit causes 502s | medium | open | Platform operator | W21 | Restart only openvibe-live.service; poll /api/ready (boot ~6s). | openvibe-live.socket is active/running |
++| H3 | Media restart during a recording orphans finalisation | high | open | Media maintainers | W4 (reconciliation + duration repair) / W7 (recording ownership to OpenRe) | Do not restart Media while a recording is active; reconcile vods-orphans/ before any retention change. | - |
++| H4 | managed_streams.stream_key values still need rotation after a past leak | high | open | Live maintainers (security track S) | W7 (openre.key.rotate) - or earlier as a standalone rotation | Ship per-stream key regeneration and rotate every key; notify affected streamers. | - |
++| H5 | Multilingual Whisper model missing breaks non-English captions | medium | mitigated | Live maintainers | - | Install the model and set WHISPER_MODEL_MULTI in live.env. | WHISPER_MODEL_MULTI is set in live.env |
++| H6 | YouTube refuses the server IP; the yt tool needs a proxy | low | open | Platform operator | W11 | Set YT_PROXY and/or YT_COOKIES_FILE for openvibe-tools-yt. | YT_PROXY still unset in tools.env |
++| H7 | Workstation root disk near full; headless Chrome can hit ENOSPC | low | open | Workstation owner | Track Q | Browser harness must support a remote/containerised runner; prune ~/.claude transcripts (~1.2 GB). | workstation / is 82% used (at generation time) |
++| H8 | A hardcoded internal secret was previously public | high | open | Platform operator (security track S) | W1 (scoped service principals) / Track S | Rotate INTERNAL_API_KEY per service and the Cloudflare token; move to scoped service tokens. | - |
++| H9 | Removed features and copy rules must not be undone | medium | standing-rule | Every product wave | all | Product waves check the prohibitions list (roadmap 9.1) before adding copy or campaigns. | - |
++| H10 | Commit identity | medium | open | Every contributor | all | Per-repo git config user.email = [email protected]; review history before push. | main history violates the rule in: Live (238 commits with assistant trailers); Network (14 commits with assistant trailers); Media (30 commits with assistant trailers); Tools (9 commits with assistant trailers) |
++| H11 | analytics_events grows without pruning | medium | open | Network maintainers (canonical openvibe-shared/analytics.js) | Track O / ADR-021 - before any schema wave touching Live or Network | Add a retention job that deletes raw rows older than the rollup window, then VACUUM in a maintenance window. | live/data/analytics.db 1.75 GB; network/data/network.db 766.5 MB; tools/apps/audio/data/analytics.db 59.9 MB |
++| H12 | No Cloudflare cache-purge step after deploys | low | open | Platform operator | W21 / Track I | Add a purge-scoped token and a purge step, or short max-age + stale-while-revalidate on non-hashed assets. | - |
++| H13 | Production drifts from main | medium | open | Platform operator | W0 (checklist) / W21 (release manifests) | Deploy or record why each lagging service is held back. | games: deployed 43c11f0 has the same file tree as origin/main 5710287 but a different SHA (pre-rewrite history); reset the checkout so later pulls fast-forward |
++| H14 | Dead and backup database files on the production host | low | open | Live maintainers | W0 | Confirm no code opens them, then delete the empty files and move the backup to /opt/backups with a retention date. | live/data/backups/live-pre-643a2e6.db, live/data/hobo.db, live/data/openvibe.db |
++
++## Constraints
++
++- **H1** (claude-context deploy-live): RTMP ingest is owned by the Live Node process; a restart disconnects every RTMP broadcaster.
++- **H2** (claude-context deploy-zero-downtime): Live uses systemd socket activation; stopping openvibe-live.socket drops the listening socket.
++- **H3** (claude-context media-vod-offload; roadmap 2.7.6): Recording/finalisation runs inside the Media process. 55 objects / 14.50 GB already sit under B2 vods-orphans/ (21 Sep 2026).
++- **H4** (claude-context seadragon-engineering-pass): Leaked keys remain valid until rotated; a rotation tool was declined earlier.
++- **H5** (claude-context japaneseoldguy-i18n): Without WHISPER_MODEL_MULTI transcribe.js falls back to the English model. _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)._
++- **H6** (claude-context tools-registry-domains): Environment problem, not code: yt downloads fail from the host address. _Evidence: 2026-09-22: neither YT_PROXY nor YT_COOKIES_FILE is set in /etc/openvibe/tools.env._
++- **H7** (claude-context seadragon-engineering-pass): Browser acceptance runs on the workstation fail when the disk fills.
++- **H8** (claude-context): Any surviving openvibe-internal-* value and the Cloudflare token must be treated as exposed.
++- **H9** (claude-context rs-promo, network-shared-systems, afresume-site, currency-model): No RobotStreamer promo campaign, no 'free/$0/no ads' copy, never 'Earn Vibes', never call the old theme 'classic'.
++- **H10** (claude-context commit-identity): Commits must be OpenVibers <[email protected]> with no assistant trailers or co-author lines.
++- **H11** (roadmap 2.7.2): Raw analytics_events is never pruned although hourly/daily rollups exist; Live analytics.db and network.db are the estate's largest stores.
++- **H12** (roadmap 2.7.5): A cacheable static asset stays stale at some edges until max-age expires; the only Cloudflare token is DNS-01 scoped.
++- **H13** (this baseline (prod-snapshot.json)): A service running an older commit than main means repo-based reasoning does not describe production.
++- **H14** (this baseline (prod-snapshot.json)): 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.
ddiff --git a/docs/roadmap-baseline/README.md b/docs/roadmap-baseline/README.md
nnew file mode 100644
iindex 00000000..cecddb85
---- /dev/null
++++ b/docs/roadmap-baseline/README.md
@@@ -0,0 +1,50 @@
++# Wave 0 baseline
++
++The audit baseline from the OpenVibe development roadmap (Wave 0; section 12 items 1-3): what exists across the estate, who owns each piece of data today and who should, what production looks like next to the repos, and which hazards constrain later waves. Every file here except `data/*.json` is generated.
++
++Source commits: Live `e31e342`, Network `71be248`, Media `6114bc6`, Tools `0346edc`, Community `de947ea`, Games `5710287`, Sites `185014f`. Production snapshot: openvibe-oregon, 2026-09-22.
++
++## Deliverables
++
++| # | Artifact | Contents |
++|---|---|---|
++| 1 | [01-services.md](01-services.md) | services, ports, units, env files, repo state, GitHub census |
++| 2 | [02-schema.md](02-schema.md) | 249 tables with current/target owner |
++| 3 | [03-routes.md](03-routes.md), [04-cross-service.md](04-cross-service.md) | 926 routes; 65 cross-service call sites |
++| 4 | [05-realtime-and-jobs.md](05-realtime-and-jobs.md) | WebSocket servers and paths, non-HTTP protocols, background jobs |
++| 5 | [06-secrets.md](06-secrets.md) | auth mechanisms and secret names (no values) |
++| 6 | [07-ownership.md](07-ownership.md) | current vs target data ownership |
++| 7 | [08-discrepancies.md](08-discrepancies.md) | 108 production-vs-repo items |
++| 8 | [09-d-status.md](09-d-status.md) | D01-D46 family status from evidence |
++| 9 | [10-hazards.md](10-hazards.md) | 14 hazards with owners, mitigations and waves |
++| - | [inventory.json](inventory.json) | everything above, machine-readable |
++
++## Exit criteria
++
++| | Criterion | Result |
++|---|---|---|
++| pass | Every table has an owner classification | 249 tables classified, 0 unclassified |
++| pass | Every route is attributed to a repo and source line | 926 routes; 0 router mounts whose module could not be resolved (their routes are listed without the mount prefix) |
++| pass | Every background job is listed with its location | 82 timers/jobs |
++| pass | Every cross-service call records caller, callee, auth, timeout and retry | 65 call sites; 30 with no timeout detected |
++| pass | Unknowns are marked unknown, not guessed | 2 unknown items recorded |
++| pass | D01-D46 status resolved from evidence | 22/22 families verified |
++| **open** | Hazard register reviewed by the production-host owner | pending: set reviewedBy in data/hazards.json after review |
++
++## Regenerating
++
++```bash
++scripts/roadmap-baseline/collect-prod.sh # read-only SSH: deployed SHAs, units, DB table names, env var NAMES
++scripts/roadmap-baseline/collect-github.sh # gh: repo list + charter STATUS.json
++node scripts/roadmap-baseline/generate.js # scan sibling checkouts, write this directory
++node scripts/roadmap-baseline/generate.js --check # also exit 1 while an exit criterion is open
++```
++
++The generator reads sibling checkouts under `OPENVIBE_ROOT` (default: the parent of this repo). Keep them on `main` and pulled before regenerating.
++
++## What this baseline does not claim
++
++- Extraction is static pattern matching. Routes built at runtime, calls through helper wrappers without an internal URL or port, and tables created by dependencies outside `vendor/openvibe-shared` can be missed. Treat counts as a floor.
++- Timeout/retry/auth detection is per file, not per request. A module that exports several routers gets every mount prefix it is loaded under.
++- No production row data, env values, provider console or payment record was read.
++- `Source.OpenVibe.Games`, `AFResume` and `BreakRoomSimulator` are not scanned; the charter repos have no code to scan.
ddiff --git a/docs/roadmap-baseline/inventory.json b/docs/roadmap-baseline/inventory.json
nnew file mode 100644
iindex 00000000..495d76c9
---- /dev/null
++++ b/docs/roadmap-baseline/inventory.json
@@@ -0,0 +1,20076 @@
++{
++ "generatedFrom": {
++ "OpenVibe.Live": "e31e3422db7e3846051aeeb07fa661b8812c123d",
++ "OpenVibe.Network": "71be248264060ca6d30d083571928f9a10002688",
++ "OpenVibe.Media": "6114bc61c466cd51335548d0ce87058df125c577",
++ "OpenVibe.Tools": "0346edc57fb9f588abcdea171a22a5ca33f772d2",
++ "OpenVibe.Community": "de947ead2addce29d7f9b98f3d894075ca4e82f2",
++ "OpenVibe.Games": "571028796639c00d5deecca038634ef1c52d3480",
++ "OpenVibe.Sites": "185014ff01ed8bd8f6df3dd5099e2b6c904e235d"
++ },
++ "prodSnapshot": {
++ "host": "openvibe-oregon",
++ "collectedAt": "2026-09-22T20:43:28.028Z"
++ },
++ "repos": [
++ {
++ "name": "OpenVibe.Live",
++ "service": "live",
++ "present": true,
++ "git": {
++ "head": "e31e3422db7e3846051aeeb07fa661b8812c123d",
++ "branch": "main",
++ "originMain": "e31e3422db7e3846051aeeb07fa661b8812c123d",
++ "committedAt": "2026-09-21T12:34:30-07:00"
++ },
++ "sourceFiles": 164,
++ "loc": 69622,
++ "tables": [
++ {
++ "table": "arena_profiles",
++ "file": "server/arena/arena-service.js",
++ "line": 82,
++ "via": "repo"
++ },
++ {
++ "table": "arena_beefs",
++ "file": "server/arena/beef.js",
++ "line": 34,
++ "via": "repo"
++ },
++ {
++ "table": "arena_beef_hype",
++ "file": "server/arena/beef.js",
++ "line": 58,
++ "via": "repo"
++ },
++ {
++ "table": "arena_beef_sides",
++ "file": "server/arena/beef.js",
++ "line": 68,
++ "via": "repo"
++ },
++ {
++ "table": "arena_trash_levels",
++ "file": "server/arena/mic.js",
++ "line": 31,
++ "via": "repo"
++ },
++ {
++ "table": "arena_xp_log",
++ "file": "server/arena/mic.js",
++ "line": 45,
++ "via": "repo"
++ },
++ {
++ "table": "arena_mic_moments",
++ "file": "server/arena/mic.js",
++ "line": 54,
++ "via": "repo"
++ },
++ {
++ "table": "dm_conversations",
++ "file": "server/chat/dm.js",
++ "line": 21,
++ "via": "repo"
++ },
++ {
++ "table": "dm_participants",
++ "file": "server/chat/dm.js",
++ "line": 31,
++ "via": "repo"
++ },
++ {
++ "table": "dm_messages",
++ "file": "server/chat/dm.js",
++ "line": 42,
++ "via": "repo"
++ },
++ {
++ "table": "dm_blocks",
++ "file": "server/chat/dm.js",
++ "line": 57,
++ "via": "repo"
++ },
++ {
++ "table": "vod_ai_state",
++ "file": "server/db/database.js",
++ "line": 263,
++ "via": "repo"
++ },
++ {
++ "table": "clip_ai_state",
++ "file": "server/db/database.js",
++ "line": 272,
++ "via": "repo"
++ },
++ {
++ "table": "site_settings",
++ "file": "server/db/database.js",
++ "line": 353,
++ "via": "repo"
++ },
++ {
++ "table": "verification_keys",
++ "file": "server/db/database.js",
++ "line": 364,
++ "via": "repo"
++ },
++ {
++ "table": "linked_accounts",
++ "file": "server/db/database.js",
++ "line": 384,
++ "via": "repo"
++ },
++ {
++ "table": "chat_messages_new",
++ "file": "server/db/database.js",
++ "line": 415,
++ "via": "repo"
++ },
++ {
++ "table": "translations",
++ "file": "server/db/database.js",
++ "line": 506,
++ "via": "repo"
++ },
++ {
++ "table": "robotstreamer_integrations",
++ "file": "server/db/database.js",
++ "line": 605,
++ "via": "repo"
++ },
++ {
++ "table": "robotstreamer_integrations_new",
++ "file": "server/db/database.js",
++ "line": 656,
++ "via": "repo"
++ },
++ {
++ "table": "restream_destinations",
++ "file": "server/db/database.js",
++ "line": 691,
++ "via": "repo"
++ },
++ {
++ "table": "platform_connections",
++ "file": "server/db/database.js",
++ "line": 709,
++ "via": "repo"
++ },
++ {
++ "table": "powerchat_connections",
++ "file": "server/db/database.js",
++ "line": 731,
++ "via": "repo"
++ },
++ {
++ "table": "powerchat_webhook_deliveries",
++ "file": "server/db/database.js",
++ "line": 748,
++ "via": "repo"
++ },
++ {
++ "table": "channel_points",
++ "file": "server/db/database.js",
++ "line": 760,
++ "via": "repo"
++ },
++ {
++ "table": "kick_channel_cache",
++ "file": "server/db/database.js",
++ "line": 774,
++ "via": "repo"
++ },
++ {
++ "table": "comments",
++ "file": "server/db/database.js",
++ "line": 849,
++ "via": "repo"
++ },
++ {
++ "table": "media_request_settings",
++ "file": "server/db/database.js",
++ "line": 869,
++ "via": "repo"
++ },
++ {
++ "table": "media_requests",
++ "file": "server/db/database.js",
++ "line": 883,
++ "via": "repo"
++ },
++ {
++ "table": "anon_ip_mappings",
++ "file": "server/db/database.js",
++ "line": 936,
++ "via": "repo"
++ },
++ {
++ "table": "app_state",
++ "file": "server/db/database.js",
++ "line": 1167,
++ "via": "repo"
++ },
++ {
++ "table": "stream_memories",
++ "file": "server/db/database.js",
++ "line": 1181,
++ "via": "repo"
++ },
++ {
++ "table": "stream_timeline_events",
++ "file": "server/db/database.js",
++ "line": 1229,
++ "via": "repo"
++ },
++ {
++ "table": "ai_usage",
++ "file": "server/db/database.js",
++ "line": 1254,
++ "via": "repo"
++ },
++ {
++ "table": "streamer_overviews",
++ "file": "server/db/database.js",
++ "line": 1274,
++ "via": "repo"
++ },
++ {
++ "table": "ai_timeline_cache",
++ "file": "server/db/database.js",
++ "line": 1296,
++ "via": "repo"
++ },
++ {
++ "table": "chat_ai_summaries",
++ "file": "server/db/database.js",
++ "line": 1308,
++ "via": "repo"
++ },
++ {
++ "table": "chat_timeline_events",
++ "file": "server/db/database.js",
++ "line": 1328,
++ "via": "repo"
++ },
++ {
++ "table": "easter_egg_solves",
++ "file": "server/db/database.js",
++ "line": 1340,
++ "via": "repo"
++ },
++ {
++ "table": "tts_voice_overrides",
++ "file": "server/db/database.js",
++ "line": 1352,
++ "via": "repo"
++ },
++ {
++ "table": "relay_users",
++ "file": "server/db/database.js",
++ "line": 1466,
++ "via": "repo"
++ },
++ {
++ "table": "payment_orders",
++ "file": "server/db/database.js",
++ "line": 1588,
++ "via": "repo"
++ },
++ {
++ "table": "channel_moderators",
++ "file": "server/db/database.js",
++ "line": 1621,
++ "via": "repo"
++ },
++ {
++ "table": "channel_moderation_settings",
++ "file": "server/db/database.js",
++ "line": 1638,
++ "via": "repo"
++ },
++ {
++ "table": "emotes_new",
++ "file": "server/db/database.js",
++ "line": 1709,
++ "via": "repo"
++ },
++ {
++ "table": "channel_sounds",
++ "file": "server/db/database.js",
++ "line": 1741,
++ "via": "repo"
++ },
++ {
++ "table": "channel_sounds_new",
++ "file": "server/db/database.js",
++ "line": 1765,
++ "via": "repo"
++ },
++ {
++ "table": "ai_chatbot_configs",
++ "file": "server/db/database.js",
++ "line": 1803,
++ "via": "repo"
++ },
++ {
++ "table": "channel_ai_bots",
++ "file": "server/db/database.js",
++ "line": 1828,
++ "via": "repo"
++ },
++ {
++ "table": "channel_ai_config",
++ "file": "server/db/database.js",
++ "line": 1852,
++ "via": "repo"
++ },
++ {
++ "table": "ai_viewer_threads",
++ "file": "server/db/database.js",
++ "line": 1873,
++ "via": "repo"
++ },
++ {
++ "table": "ai_viewer_log",
++ "file": "server/db/database.js",
++ "line": 1887,
++ "via": "repo"
++ },
++ {
++ "table": "moderation_actions",
++ "file": "server/db/database.js",
++ "line": 1920,
++ "via": "repo"
++ },
++ {
++ "table": "pastes",
++ "file": "server/db/database.js",
++ "line": 1939,
++ "via": "repo"
++ },
++ {
++ "table": "paste_likes",
++ "file": "server/db/database.js",
++ "line": 1976,
++ "via": "repo"
++ },
++ {
++ "table": "paste_comments",
++ "file": "server/db/database.js",
++ "line": 2008,
++ "via": "repo"
++ },
++ {
++ "table": "stream_first_chats",
++ "file": "server/db/database.js",
++ "line": 2040,
++ "via": "repo"
++ },
++ {
++ "table": "ip_log",
++ "file": "server/db/database.js",
++ "line": 2051,
++ "via": "repo"
++ },
++ {
++ "table": "approved_ips",
++ "file": "server/db/database.js",
++ "line": 2075,
++ "via": "repo"
++ },
++ {
++ "table": "pending_ip_messages",
++ "file": "server/db/database.js",
++ "line": 2092,
++ "via": "repo"
++ },
++ {
++ "table": "hidden_relay_users",
++ "file": "server/db/database.js",
++ "line": 2112,
++ "via": "repo"
++ },
++ {
++ "table": "control_whitelist",
++ "file": "server/db/database.js",
++ "line": 2277,
++ "via": "repo"
++ },
++ {
++ "table": "control_configs",
++ "file": "server/db/database.js",
++ "line": 2292,
++ "via": "repo"
++ },
++ {
++ "table": "control_config_buttons",
++ "file": "server/db/database.js",
++ "line": 2306,
++ "via": "repo"
++ },
++ {
++ "table": "stream_controls_new",
++ "file": "server/db/database.js",
++ "line": 2385,
++ "via": "repo"
++ },
++ {
++ "table": "managed_streams",
++ "file": "server/db/database.js",
++ "line": 2420,
++ "via": "repo"
++ },
++ {
++ "table": "vibe_coding_sessions",
++ "file": "server/db/database.js",
++ "line": 2642,
++ "via": "repo"
++ },
++ {
++ "table": "vibe_coding_events",
++ "file": "server/db/database.js",
++ "line": 2677,
++ "via": "repo"
++ },
++ {
++ "table": "viewer_samples",
++ "file": "server/db/database.js",
++ "line": 3956,
++ "via": "repo"
++ },
++ {
++ "table": "schema_migrations",
++ "file": "server/db/migrations.js",
++ "line": 27,
++ "via": "repo"
++ },
++ {
++ "table": "users",
++ "file": "server/db/schema.sql",
++ "line": 5,
++ "via": "repo"
++ },
++ {
++ "table": "channels",
++ "file": "server/db/schema.sql",
++ "line": 26,
++ "via": "repo"
++ },
++ {
++ "table": "robotstreamer_integrations",
++ "file": "server/db/schema.sql",
++ "line": 58,
++ "via": "repo"
++ },
++ {
++ "table": "managed_streams",
++ "file": "server/db/schema.sql",
++ "line": 78,
++ "via": "repo"
++ },
++ {
++ "table": "streams",
++ "file": "server/db/schema.sql",
++ "line": 103,
++ "via": "repo"
++ },
++ {
++ "table": "cameras",
++ "file": "server/db/schema.sql",
++ "line": 133,
++ "via": "repo"
++ },
++ {
++ "table": "chat_messages",
++ "file": "server/db/schema.sql",
++ "line": 148,
++ "via": "repo"
++ },
++ {
++ "table": "vibe_coding_sessions",
++ "file": "server/db/schema.sql",
++ "line": 170,
++ "via": "repo"
++ },
++ {
++ "table": "vibe_coding_events",
++ "file": "server/db/schema.sql",
++ "line": 198,
++ "via": "repo"
++ },
++ {
++ "table": "follows",
++ "file": "server/db/schema.sql",
++ "line": 221,
++ "via": "repo"
++ },
++ {
++ "table": "subscriptions",
++ "file": "server/db/schema.sql",
++ "line": 234,
++ "via": "repo"
++ },
++ {
++ "table": "transactions",
++ "file": "server/db/schema.sql",
++ "line": 247,
++ "via": "repo"
++ },
++ {
++ "table": "donation_goals",
++ "file": "server/db/schema.sql",
++ "line": 264,
++ "via": "repo"
++ },
++ {
++ "table": "vods",
++ "file": "server/db/schema.sql",
++ "line": 280,
++ "via": "repo"
++ },
++ {
++ "table": "clips",
++ "file": "server/db/schema.sql",
++ "line": 307,
++ "via": "repo"
++ },
++ {
++ "table": "content_views",
++ "file": "server/db/schema.sql",
++ "line": 328,
++ "via": "repo"
++ },
++ {
++ "table": "camera_profiles",
++ "file": "server/db/schema.sql",
++ "line": 339,
++ "via": "repo"
++ },
++ {
++ "table": "camera_presets",
++ "file": "server/db/schema.sql",
++ "line": 359,
++ "via": "repo"
++ },
++ {
++ "table": "stream_controls",
++ "file": "server/db/schema.sql",
++ "line": 372,
++ "via": "repo"
++ },
++ {
++ "table": "api_keys",
++ "file": "server/db/schema.sql",
++ "line": 391,
++ "via": "repo"
++ },
++ {
++ "table": "api_tokens",
++ "file": "server/db/schema.sql",
++ "line": 404,
++ "via": "repo"
++ },
++ {
++ "table": "bans",
++ "file": "server/db/schema.sql",
++ "line": 418,
++ "via": "repo"
++ },
++ {
++ "table": "vpn_approvals",
++ "file": "server/db/schema.sql",
++ "line": 434,
++ "via": "repo"
++ },
++ {
++ "table": "themes",
++ "file": "server/db/schema.sql",
++ "line": 446,
++ "via": "repo"
++ },
++ {
++ "table": "user_themes",
++ "file": "server/db/schema.sql",
++ "line": 467,
++ "via": "repo"
++ },
++ {
++ "table": "channel_moderators",
++ "file": "server/db/schema.sql",
++ "line": 499,
++ "via": "repo"
++ },
++ {
++ "table": "channel_moderation_settings",
++ "file": "server/db/schema.sql",
++ "line": 514,
++ "via": "repo"
++ },
++ {
++ "table": "site_settings",
++ "file": "server/db/schema.sql",
++ "line": 556,
++ "via": "repo"
++ },
++ {
++ "table": "verification_keys",
++ "file": "server/db/schema.sql",
++ "line": 567,
++ "via": "repo"
++ },
++ {
++ "table": "comments",
++ "file": "server/db/schema.sql",
++ "line": 585,
++ "via": "repo"
++ },
++ {
++ "table": "emotes",
++ "file": "server/db/schema.sql",
++ "line": 603,
++ "via": "repo"
++ },
++ {
++ "table": "channel_sounds",
++ "file": "server/db/schema.sql",
++ "line": 625,
++ "via": "repo"
++ },
++ {
++ "table": "coin_transactions",
++ "file": "server/db/schema.sql",
++ "line": 651,
++ "via": "repo"
++ },
++ {
++ "table": "coin_rewards",
++ "file": "server/db/schema.sql",
++ "line": 668,
++ "via": "repo"
++ },
++ {
++ "table": "coin_redemptions",
++ "file": "server/db/schema.sql",
++ "line": 689,
++ "via": "repo"
++ },
++ {
++ "table": "watch_time",
++ "file": "server/db/schema.sql",
++ "line": 706,
++ "via": "repo"
++ },
++ {
++ "table": "media_request_settings",
++ "file": "server/db/schema.sql",
++ "line": 722,
++ "via": "repo"
++ },
++ {
++ "table": "media_requests",
++ "file": "server/db/schema.sql",
++ "line": 739,
++ "via": "repo"
++ },
++ {
++ "table": "user_cosmetics",
++ "file": "server/db/schema.sql",
++ "line": 774,
++ "via": "repo"
++ },
++ {
++ "table": "user_equipped",
++ "file": "server/db/schema.sql",
++ "line": 786,
++ "via": "repo"
++ },
++ {
++ "table": "viewer_snapshots",
++ "file": "server/db/schema.sql",
++ "line": 799,
++ "via": "repo"
++ },
++ {
++ "table": "stream_analytics",
++ "file": "server/db/schema.sql",
++ "line": 810,
++ "via": "repo"
++ },
++ {
++ "table": "user_preferences",
++ "file": "server/db/schema.sql",
++ "line": 825,
++ "via": "repo"
++ },
++ {
++ "table": "game_fish_collection",
++ "file": "server/game/game-engine.js",
++ "line": 522,
++ "via": "repo"
++ },
++ {
++ "table": "game_achievements",
++ "file": "server/game/game-engine.js",
++ "line": 536,
++ "via": "repo"
++ },
++ {
++ "table": "game_daily_quest_progress",
++ "file": "server/game/game-engine.js",
++ "line": 546,
++ "via": "repo"
++ },
++ {
++ "table": "game_daily_quest_claims",
++ "file": "server/game/game-engine.js",
++ "line": 557,
++ "via": "repo"
++ },
++ {
++ "table": "game_world_state",
++ "file": "server/game/schema.sql",
++ "line": 7,
++ "via": "repo"
++ },
++ {
++ "table": "game_players",
++ "file": "server/game/schema.sql",
++ "line": 13,
++ "via": "repo"
++ },
++ {
++ "table": "game_inventory",
++ "file": "server/game/schema.sql",
++ "line": 66,
++ "via": "repo"
++ },
++ {
++ "table": "game_bank",
++ "file": "server/game/schema.sql",
++ "line": 77,
++ "via": "repo"
++ },
++ {
++ "table": "game_structures",
++ "file": "server/game/schema.sql",
++ "line": 87,
++ "via": "repo"
++ },
++ {
++ "table": "game_farm_plots",
++ "file": "server/game/schema.sql",
++ "line": 104,
++ "via": "repo"
++ },
++ {
++ "table": "game_recipes",
++ "file": "server/game/schema.sql",
++ "line": 118,
++ "via": "repo"
++ },
++ {
++ "table": "game_effects",
++ "file": "server/game/schema.sql",
++ "line": 128,
++ "via": "repo"
++ },
++ {
++ "table": "game_battle_stats",
++ "file": "server/game/schema.sql",
++ "line": 142,
++ "via": "repo"
++ },
++ {
++ "table": "game_dungeon_runs",
++ "file": "server/game/schema.sql",
++ "line": 156,
++ "via": "repo"
++ },
++ {
++ "table": "game_leaderboard",
++ "file": "server/game/schema.sql",
++ "line": 168,
++ "via": "repo"
++ },
++ {
++ "table": "game_fish_collection",
++ "file": "server/game/schema.sql",
++ "line": 180,
++ "via": "repo"
++ },
++ {
++ "table": "game_daily_quest_progress",
++ "file": "server/game/schema.sql",
++ "line": 192,
++ "via": "repo"
++ },
++ {
++ "table": "game_daily_quest_claims",
++ "file": "server/game/schema.sql",
++ "line": 203,
++ "via": "repo"
++ },
++ {
++ "table": "user_tags",
++ "file": "server/game/tags.js",
++ "line": 67,
++ "via": "repo"
++ },
++ {
++ "table": "user_equipped_tag",
++ "file": "server/game/tags.js",
++ "line": 76,
++ "via": "repo"
++ },
++ {
++ "table": "tag_guardian_defeats",
++ "file": "server/game/tags.js",
++ "line": 81,
++ "via": "repo"
++ },
++ {
++ "table": "translations",
++ "file": "server/i18n/translate.js",
++ "line": 145,
++ "via": "repo"
++ },
++ {
++ "table": "user_cosmetics",
++ "file": "server/monetization/cosmetics.js",
++ "line": 109,
++ "via": "repo"
++ },
++ {
++ "table": "user_equipped",
++ "file": "server/monetization/cosmetics.js",
++ "line": 118,
++ "via": "repo"
++ },
++ {
++ "table": "news_settings",
++ "file": "server/news/news-service.js",
++ "line": 47,
++ "via": "repo"
++ },
++ {
++ "table": "stream_recaps",
++ "file": "server/recap/recap.js",
++ "line": 27,
++ "via": "repo"
++ },
++ {
++ "table": "analytics_events",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 74,
++ "via": "vendored openvibe-shared"
++ },
++ {
++ "table": "analytics_hourly",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 98,
++ "via": "vendored openvibe-shared"
++ },
++ {
++ "table": "analytics_daily",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 116,
++ "via": "vendored openvibe-shared"
++ },
++ {
++ "table": "analytics_rate_tracking",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 137,
++ "via": "vendored openvibe-shared"
++ }
++ ],
++ "routes": [
++ {
++ "method": "GET",
++ "path": "/api/admin/admins",
++ "file": "server/admin/routes.js",
++ "line": 762
++ },
++ {
++ "method": "POST",
++ "path": "/api/admin/admins",
++ "file": "server/admin/routes.js",
++ "line": 772
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/admin/admins/:id",
++ "file": "server/admin/routes.js",
++ "line": 791
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/ai/explorer/:userId",
++ "file": "server/admin/routes.js",
++ "line": 551
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/ai/overviews",
++ "file": "server/admin/routes.js",
++ "line": 583
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/ai/status",
++ "file": "server/admin/routes.js",
++ "line": 541
++ },
++ {
++ "method": "POST",
++ "path": "/api/admin/ai/streamer/:userId/overview",
++ "file": "server/admin/routes.js",
++ "line": 592
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/ai/usage",
++ "file": "server/admin/routes.js",
++ "line": 494
++ },
++ {
++ "method": "POST",
++ "path": "/api/admin/ai/viewers/:userId/:action",
++ "file": "server/admin/routes.js",
++ "line": 522
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/ai/viewers/:userId/log",
++ "file": "server/admin/routes.js",
++ "line": 532
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/ai/viewers/status",
++ "file": "server/admin/routes.js",
++ "line": 504
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/analytics",
++ "file": "server/index.js",
++ "line": 708
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/analytics/bots",
++ "file": "server/index.js",
++ "line": 717
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/bans",
++ "file": "server/admin/routes.js",
++ "line": 429
++ },
++ {
++ "method": "POST",
++ "path": "/api/admin/broadcast",
++ "file": "server/index.js",
++ "line": 826
++ },
++ {
++ "method": "PUT",
++ "path": "/api/admin/channels/:id/force-nsfw",
++ "file": "server/admin/routes.js",
++ "line": 359
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/diagnostics",
++ "file": "server/admin/routes.js",
++ "line": 51
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/admin/media-tools/cookies",
++ "file": "server/admin/routes.js",
++ "line": 1214
++ },
++ {
++ "method": "PUT",
++ "path": "/api/admin/media-tools/cookies",
++ "file": "server/admin/routes.js",
++ "line": 1133
++ },
++ {
++ "method": "POST",
++ "path": "/api/admin/media-tools/cookies/check",
++ "file": "server/admin/routes.js",
++ "line": 1192
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/admin/media-tools/extra-args",
++ "file": "server/admin/routes.js",
++ "line": 1247
++ },
++ {
++ "method": "PUT",
++ "path": "/api/admin/media-tools/extra-args",
++ "file": "server/admin/routes.js",
++ "line": 1228
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/media-tools/status",
++ "file": "server/admin/routes.js",
++ "line": 1088
++ },
++ {
++ "method": "POST",
++ "path": "/api/admin/media-tools/test",
++ "file": "server/admin/routes.js",
++ "line": 1258
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/moderators",
++ "file": "server/admin/routes.js",
++ "line": 697
++ },
++ {
++ "method": "POST",
++ "path": "/api/admin/moderators",
++ "file": "server/admin/routes.js",
++ "line": 709
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/admin/moderators/:id",
++ "file": "server/admin/routes.js",
++ "line": 736
++ },
++ {
++ "method": "PUT",
++ "path": "/api/admin/pastes/:id/nsfw",
++ "file": "server/admin/routes.js",
++ "line": 418
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/settings",
++ "file": "server/admin/routes.js",
++ "line": 482
++ },
++ {
++ "method": "PUT",
++ "path": "/api/admin/settings",
++ "file": "server/admin/routes.js",
++ "line": 607
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/admin/settings/:key",
++ "file": "server/admin/routes.js",
++ "line": 680
++ },
++ {
++ "method": "PUT",
++ "path": "/api/admin/settings/:key",
++ "file": "server/admin/routes.js",
++ "line": 663
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/stats",
++ "file": "server/admin/routes.js",
++ "line": 63
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/storage",
++ "file": "server/admin/routes.js",
++ "line": 935
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/admin/storage/clips/bulk",
++ "file": "server/admin/routes.js",
++ "line": 1038
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/storage/vods",
++ "file": "server/admin/routes.js",
++ "line": 989
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/admin/storage/vods/bulk",
++ "file": "server/admin/routes.js",
++ "line": 1008
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/streams",
++ "file": "server/admin/routes.js",
++ "line": 294
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/admin/streams/:id",
++ "file": "server/admin/routes.js",
++ "line": 313
++ },
++ {
++ "method": "PUT",
++ "path": "/api/admin/streams/:id/nsfw",
++ "file": "server/admin/routes.js",
++ "line": 407
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/users",
++ "file": "server/admin/routes.js",
++ "line": 111
++ },
++ {
++ "method": "PUT",
++ "path": "/api/admin/users/:id",
++ "file": "server/admin/routes.js",
++ "line": 147
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/admin/users/:id/ban",
++ "file": "server/admin/routes.js",
++ "line": 275
++ },
++ {
++ "method": "POST",
++ "path": "/api/admin/users/:id/ban",
++ "file": "server/admin/routes.js",
++ "line": 237
++ },
++ {
++ "method": "PUT",
++ "path": "/api/admin/users/:id/force-vod-recording",
++ "file": "server/admin/routes.js",
++ "line": 376
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/verification-keys",
++ "file": "server/admin/routes.js",
++ "line": 813
++ },
++ {
++ "method": "POST",
++ "path": "/api/admin/verification-keys",
++ "file": "server/admin/routes.js",
++ "line": 823
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/admin/verification-keys/:id",
++ "file": "server/admin/routes.js",
++ "line": 866
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/vpn-queue",
++ "file": "server/admin/routes.js",
++ "line": 445
++ },
++ {
++ "method": "PUT",
++ "path": "/api/admin/vpn-queue/:id",
++ "file": "server/admin/routes.js",
++ "line": 461
++ },
++ {
++ "method": "POST",
++ "path": "/api/ai-viewers/${cmd}",
++ "file": "server/ai/viewers/routes.js",
++ "line": 117
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/ai-viewers/bots/:id",
++ "file": "server/ai/viewers/routes.js",
++ "line": 220
++ },
++ {
++ "method": "PATCH",
++ "path": "/api/ai-viewers/bots/:id",
++ "file": "server/ai/viewers/routes.js",
++ "line": 185
++ },
++ {
++ "method": "POST",
++ "path": "/api/ai-viewers/bots/:id/clear-memory",
++ "file": "server/ai/viewers/routes.js",
++ "line": 209
++ },
++ {
++ "method": "POST",
++ "path": "/api/ai-viewers/bots/:id/mute",
++ "file": "server/ai/viewers/routes.js",
++ "line": 122
++ },
++ {
++ "method": "POST",
++ "path": "/api/ai-viewers/bots/:id/unmute",
++ "file": "server/ai/viewers/routes.js",
++ "line": 123
++ },
++ {
++ "method": "POST",
++ "path": "/api/ai-viewers/byo/test",
++ "file": "server/ai/viewers/routes.js",
++ "line": 124
++ },
++ {
++ "method": "POST",
++ "path": "/api/ai-viewers/clone",
++ "file": "server/ai/viewers/routes.js",
++ "line": 233
++ },
++ {
++ "method": "GET",
++ "path": "/api/ai-viewers/config",
++ "file": "server/ai/viewers/routes.js",
++ "line": 82
++ },
++ {
++ "method": "PUT",
++ "path": "/api/ai-viewers/config",
++ "file": "server/ai/viewers/routes.js",
++ "line": 134
++ },
++ {
++ "method": "GET",
++ "path": "/api/ai-viewers/log",
++ "file": "server/ai/viewers/routes.js",
++ "line": 106
++ },
++ {
++ "method": "POST",
++ "path": "/api/ai-viewers/preview",
++ "file": "server/ai/viewers/routes.js",
++ "line": 282
++ },
++ {
++ "method": "GET",
++ "path": "/api/ai-viewers/roster",
++ "file": "server/ai/viewers/routes.js",
++ "line": 167
++ },
++ {
++ "method": "GET",
++ "path": "/api/ai-viewers/status",
++ "file": "server/ai/viewers/routes.js",
++ "line": 99
++ },
++ {
++ "method": "GET",
++ "path": "/api/ai-viewers/threads",
++ "file": "server/ai/viewers/routes.js",
++ "line": 112
++ },
++ {
++ "method": "GET",
++ "path": "/api/analytics/channel/:username",
++ "file": "server/streaming/analytics-routes.js",
++ "line": 22
++ },
++ {
++ "method": "GET",
++ "path": "/api/analytics/channel/:username/dashboard",
++ "file": "server/streaming/analytics-routes.js",
++ "line": 156
++ },
++ {
++ "method": "GET",
++ "path": "/api/analytics/channel/:username/streams",
++ "file": "server/streaming/analytics-routes.js",
++ "line": 57
++ },
++ {
++ "method": "GET",
++ "path": "/api/analytics/stream/:id",
++ "file": "server/streaming/analytics-routes.js",
++ "line": 96
++ },
++ {
++ "method": "POST",
++ "path": "/api/arena/backfill",
++ "file": "server/arena/routes.js",
++ "line": 80
++ },
++ {
++ "method": "GET",
++ "path": "/api/arena/beefs",
++ "file": "server/arena/routes.js",
++ "line": 112
++ },
++ {
++ "method": "GET",
++ "path": "/api/arena/beefs/:id",
++ "file": "server/arena/routes.js",
++ "line": 113
++ },
++ {
++ "method": "POST",
++ "path": "/api/arena/beefs/:id/hype",
++ "file": "server/arena/routes.js",
++ "line": 117
++ },
++ {
++ "method": "GET",
++ "path": "/api/arena/console/:user",
++ "file": "server/arena/routes.js",
++ "line": 85
++ },
++ {
++ "method": "GET",
++ "path": "/api/arena/feed",
++ "file": "server/arena/routes.js",
++ "line": 37
++ },
++ {
++ "method": "GET",
++ "path": "/api/arena/fighters",
++ "file": "server/arena/routes.js",
++ "line": 32
++ },
++ {
++ "method": "GET",
++ "path": "/api/arena/fighters/:user",
++ "file": "server/arena/routes.js",
++ "line": 56
++ },
++ {
++ "method": "POST",
++ "path": "/api/arena/fighters/:user/refresh",
++ "file": "server/arena/routes.js",
++ "line": 69
++ },
++ {
++ "method": "GET",
++ "path": "/api/arena/fighters/:user/stat/:stat",
++ "file": "server/arena/routes.js",
++ "line": 46
++ },
++ {
++ "method": "GET",
++ "path": "/api/arena/levels",
++ "file": "server/arena/routes.js",
++ "line": 150
++ },
++ {
++ "method": "GET",
++ "path": "/api/arena/live",
++ "file": "server/arena/routes.js",
++ "line": 36
++ },
++ {
++ "method": "GET",
++ "path": "/api/arena/me",
++ "file": "server/arena/routes.js",
++ "line": 138
++ },
++ {
++ "method": "GET",
++ "path": "/api/arena/status",
++ "file": "server/arena/routes.js",
++ "line": 30
++ },
++ {
++ "method": "GET",
++ "path": "/api/arena/voice/:user",
++ "file": "server/arena/routes.js",
++ "line": 122
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/avatar",
++ "file": "server/auth/routes.js",
++ "line": 192
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/avatar/history",
++ "file": "server/auth/routes.js",
++ "line": 246
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/callback",
++ "file": "server/auth/routes.js",
++ "line": 488
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/change-password",
++ "file": "server/auth/routes.js",
++ "line": 166
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/fedcm",
++ "file": "server/auth/routes.js",
++ "line": 465
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/ice-servers",
++ "file": "server/auth/routes.js",
++ "line": 898
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/login",
++ "file": "server/auth/routes.js",
++ "line": 97
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/logout",
++ "file": "server/auth/routes.js",
++ "line": 627
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/logout",
++ "file": "server/auth/routes.js",
++ "line": 620
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/me",
++ "file": "server/auth/routes.js",
++ "line": 102
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/preferences",
++ "file": "server/auth/routes.js",
++ "line": 677
++ },
++ {
++ "method": "PUT",
++ "path": "/api/auth/preferences",
++ "file": "server/auth/routes.js",
++ "line": 687
++ },
++ {
++ "method": "PUT",
++ "path": "/api/auth/profile",
++ "file": "server/auth/routes.js",
++ "line": 110
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/refresh",
++ "file": "server/auth/routes.js",
++ "line": 558
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/register",
++ "file": "server/auth/routes.js",
++ "line": 92
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/sso/login",
++ "file": "server/auth/routes.js",
++ "line": 312
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/sso/status",
++ "file": "server/auth/routes.js",
++ "line": 638
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/stream-key",
++ "file": "server/auth/routes.js",
++ "line": 171
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/stream-key/regenerate",
++ "file": "server/auth/routes.js",
++ "line": 176
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/tokens",
++ "file": "server/auth/routes.js",
++ "line": 857
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/tokens",
++ "file": "server/auth/routes.js",
++ "line": 819
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/auth/tokens/:id",
++ "file": "server/auth/routes.js",
++ "line": 877
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/user/:username",
++ "file": "server/auth/routes.js",
++ "line": 183
++ },
++ {
++ "method": "GET",
++ "path": "/api/channels/:channelId/moderation",
++ "file": "server/admin/channel-mod-routes.js",
++ "line": 157
++ },
++ {
++ "method": "PUT",
++ "path": "/api/channels/:channelId/moderation",
++ "file": "server/admin/channel-mod-routes.js",
++ "line": 170
++ },
++ {
++ "method": "GET",
++ "path": "/api/channels/:channelId/moderation/chat-search",
++ "file": "server/admin/channel-mod-routes.js",
++ "line": 306
++ },
++ {
++ "method": "GET",
++ "path": "/api/channels/:channelId/moderation/logs",
++ "file": "server/admin/channel-mod-routes.js",
++ "line": 288
++ },
++ {
++ "method": "POST",
++ "path": "/api/channels/:channelId/moderation/messages/:messageId/delete",
++ "file": "server/admin/channel-mod-routes.js",
++ "line": 320
++ },
++ {
++ "method": "GET",
++ "path": "/api/channels/:channelId/mods",
++ "file": "server/admin/channel-mod-routes.js",
++ "line": 75
++ },
++ {
++ "method": "POST",
++ "path": "/api/channels/:channelId/mods",
++ "file": "server/admin/channel-mod-routes.js",
++ "line": 89
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/channels/:channelId/mods/:userId",
++ "file": "server/admin/channel-mod-routes.js",
++ "line": 127
++ },
++ {
++ "method": "GET",
++ "path": "/api/channels/moderation/mine",
++ "file": "server/admin/channel-mod-routes.js",
++ "line": 49
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat-ai/anon/:anonId",
++ "file": "server/ai/chat-ai-routes.js",
++ "line": 83
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat-ai/global",
++ "file": "server/ai/chat-ai-routes.js",
++ "line": 14
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat-ai/live-captions/:username",
++ "file": "server/ai/chat-ai-routes.js",
++ "line": 230
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat-ai/relay/:platform/:username",
++ "file": "server/ai/chat-ai-routes.js",
++ "line": 95
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat-ai/timeline",
++ "file": "server/ai/chat-ai-routes.js",
++ "line": 24
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat-ai/timeline/:username",
++ "file": "server/ai/chat-ai-routes.js",
++ "line": 174
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat-ai/transcript/:streamId",
++ "file": "server/ai/chat-ai-routes.js",
++ "line": 322
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat-ai/user/:id",
++ "file": "server/ai/chat-ai-routes.js",
++ "line": 39
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat-ai/vod-transcripts",
++ "file": "server/ai/chat-ai-routes.js",
++ "line": 267
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/:streamId/history",
++ "file": "server/chat/routes.js",
++ "line": 509
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/:streamId/replay",
++ "file": "server/chat/routes.js",
++ "line": 487
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/:streamId/users",
++ "file": "server/chat/routes.js",
++ "line": 633
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/admin/logs",
++ "file": "server/chat/routes.js",
++ "line": 718
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/admin/logs/export",
++ "file": "server/chat/routes.js",
++ "line": 755
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/chat/admin/purge",
++ "file": "server/chat/routes.js",
++ "line": 674
++ },
++ {
++ "method": "POST",
++ "path": "/api/chat/admin/purge/preview",
++ "file": "server/chat/routes.js",
++ "line": 642
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/anon/:anonId",
++ "file": "server/chat/routes.js",
++ "line": 419
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/anon/:anonId/logs",
++ "file": "server/chat/routes.js",
++ "line": 430
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/channel/:userId/history",
++ "file": "server/chat/routes.js",
++ "line": 609
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/filters/friendly",
++ "file": "server/chat/routes.js",
++ "line": 462
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/gif/providers",
++ "file": "server/chat/routes.js",
++ "line": 109
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/gif/search",
++ "file": "server/chat/routes.js",
++ "line": 145
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/gif/trending",
++ "file": "server/chat/routes.js",
++ "line": 119
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/global/history",
++ "file": "server/chat/routes.js",
++ "line": 468
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/relay-user/:platform/:username",
++ "file": "server/chat/routes.js",
++ "line": 409
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/relay-user/:platform/:username/logs",
++ "file": "server/chat/routes.js",
++ "line": 446
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/search",
++ "file": "server/chat/routes.js",
++ "line": 319
++ },
++ {
++ "method": "POST",
++ "path": "/api/chat/send",
++ "file": "server/chat/routes.js",
++ "line": 209
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/user/:userId/history",
++ "file": "server/chat/routes.js",
++ "line": 341
++ },
++ {
++ "method": "GET",
++ "path": "/api/chat/user/:username/profile",
++ "file": "server/chat/routes.js",
++ "line": 360
++ },
++ {
++ "method": "GET",
++ "path": "/api/clips",
++ "file": "server/media-proxy/clips.js",
++ "line": 149
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/clips/:id",
++ "file": "server/media-proxy/clips.js",
++ "line": 317
++ },
++ {
++ "method": "GET",
++ "path": "/api/clips/:id",
++ "file": "server/media-proxy/clips.js",
++ "line": 178
++ },
++ {
++ "method": "POST",
++ "path": "/api/clips/:id/recut",
++ "file": "server/media-proxy/clips.js",
++ "line": 335
++ },
++ {
++ "method": "PUT",
++ "path": "/api/clips/:id/title",
++ "file": "server/media-proxy/clips.js",
++ "line": 251
++ },
++ {
++ "method": "PUT",
++ "path": "/api/clips/:id/visibility",
++ "file": "server/media-proxy/clips.js",
++ "line": 268
++ },
++ {
++ "method": "POST",
++ "path": "/api/clips/bulk",
++ "file": "server/media-proxy/clips.js",
++ "line": 292
++ },
++ {
++ "method": "GET",
++ "path": "/api/clips/mine",
++ "file": "server/media-proxy/clips.js",
++ "line": 101
++ },
++ {
++ "method": "GET",
++ "path": "/api/clips/my-stream",
++ "file": "server/media-proxy/clips.js",
++ "line": 115
++ },
++ {
++ "method": "GET",
++ "path": "/api/clips/settings/channel",
++ "file": "server/media-proxy/clips.js",
++ "line": 127
++ },
++ {
++ "method": "PUT",
++ "path": "/api/clips/settings/channel",
++ "file": "server/media-proxy/clips.js",
++ "line": 136
++ },
++ {
++ "method": "POST",
++ "path": "/api/coins/admin/grant",
++ "file": "server/monetization/coins-routes.js",
++ "line": 310
++ },
++ {
++ "method": "GET",
++ "path": "/api/coins/balance",
++ "file": "server/monetization/coins-routes.js",
++ "line": 26
++ },
++ {
++ "method": "POST",
++ "path": "/api/coins/bonus",
++ "file": "server/monetization/coins-routes.js",
++ "line": 70
++ },
++ {
++ "method": "GET",
++ "path": "/api/coins/channel-balance",
++ "file": "server/monetization/coins-routes.js",
++ "line": 31
++ },
++ {
++ "method": "PUT",
++ "path": "/api/coins/config",
++ "file": "server/monetization/coins-routes.js",
++ "line": 95
++ },
++ {
++ "method": "GET",
++ "path": "/api/coins/config/:userId",
++ "file": "server/monetization/coins-routes.js",
++ "line": 90
++ },
++ {
++ "method": "POST",
++ "path": "/api/coins/heartbeat",
++ "file": "server/monetization/coins-routes.js",
++ "line": 52
++ },
++ {
++ "method": "GET",
++ "path": "/api/coins/history",
++ "file": "server/monetization/coins-routes.js",
++ "line": 44
++ },
++ {
++ "method": "GET",
++ "path": "/api/coins/rates",
++ "file": "server/monetization/coins-routes.js",
++ "line": 37
++ },
++ {
++ "method": "POST",
++ "path": "/api/coins/redeem",
++ "file": "server/monetization/coins-routes.js",
++ "line": 207
++ },
++ {
++ "method": "GET",
++ "path": "/api/coins/redemptions",
++ "file": "server/monetization/coins-routes.js",
++ "line": 261
++ },
++ {
++ "method": "POST",
++ "path": "/api/coins/redemptions/:id",
++ "file": "server/monetization/coins-routes.js",
++ "line": 267
++ },
++ {
++ "method": "POST",
++ "path": "/api/coins/rewards",
++ "file": "server/monetization/coins-routes.js",
++ "line": 116
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/coins/rewards/:id",
++ "file": "server/monetization/coins-routes.js",
++ "line": 193
++ },
++ {
++ "method": "PUT",
++ "path": "/api/coins/rewards/:id",
++ "file": "server/monetization/coins-routes.js",
++ "line": 154
++ },
++ {
++ "method": "GET",
++ "path": "/api/coins/rewards/:userId",
++ "file": "server/monetization/coins-routes.js",
++ "line": 83
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/comments/:commentId",
++ "file": "server/media-proxy/comments.js",
++ "line": 178
++ },
++ {
++ "method": "PUT",
++ "path": "/api/comments/:commentId",
++ "file": "server/media-proxy/comments.js",
++ "line": 159
++ },
++ {
++ "method": "GET",
++ "path": "/api/comments/:commentId/replies",
++ "file": "server/media-proxy/comments.js",
++ "line": 149
++ },
++ {
++ "method": "GET",
++ "path": "/api/comments/:type/:id",
++ "file": "server/media-proxy/comments.js",
++ "line": 40
++ },
++ {
++ "method": "POST",
++ "path": "/api/comments/:type/:id",
++ "file": "server/media-proxy/comments.js",
++ "line": 64
++ },
++ {
++ "method": "GET",
++ "path": "/api/controls/:streamId",
++ "file": "server/controls/routes.js",
++ "line": 1460
++ },
++ {
++ "method": "GET",
++ "path": "/api/controls/:streamId/config",
++ "file": "server/controls/routes.js",
++ "line": 1417
++ },
++ {
++ "method": "PUT",
++ "path": "/api/controls/:streamId/config",
++ "file": "server/controls/routes.js",
++ "line": 1431
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/controls/:streamId/presets/cozmo",
++ "file": "server/controls/routes.js",
++ "line": 1507
++ },
++ {
++ "method": "POST",
++ "path": "/api/controls/:streamId/presets/cozmo",
++ "file": "server/controls/routes.js",
++ "line": 1493
++ },
++ {
++ "method": "POST",
++ "path": "/api/controls/api-key",
++ "file": "server/controls/routes.js",
++ "line": 1228
++ },
++ {
++ "method": "GET",
++ "path": "/api/controls/api-keys",
++ "file": "server/controls/routes.js",
++ "line": 1255
++ },
++ {
++ "method": "GET",
++ "path": "/api/controls/configs",
++ "file": "server/controls/routes.js",
++ "line": 979
++ },
++ {
++ "method": "POST",
++ "path": "/api/controls/configs",
++ "file": "server/controls/routes.js",
++ "line": 994
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/controls/configs/:id",
++ "file": "server/controls/routes.js",
++ "line": 1053
++ },
++ {
++ "method": "GET",
++ "path": "/api/controls/configs/:id",
++ "file": "server/controls/routes.js",
++ "line": 1018
++ },
++ {
++ "method": "PUT",
++ "path": "/api/controls/configs/:id",
++ "file": "server/controls/routes.js",
++ "line": 1033
++ },
++ {
++ "method": "POST",
++ "path": "/api/controls/configs/:id/activate",
++ "file": "server/controls/routes.js",
++ "line": 1183
++ },
++ {
++ "method": "POST",
++ "path": "/api/controls/configs/:id/apply/:streamId",
++ "file": "server/controls/routes.js",
++ "line": 1208
++ },
++ {
++ "method": "GET",
++ "path": "/api/controls/configs/:id/bridge-script",
++ "file": "server/controls/routes.js",
++ "line": 1391
++ },
++ {
++ "method": "POST",
++ "path": "/api/controls/configs/:id/buttons",
++ "file": "server/controls/routes.js",
++ "line": 1073
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/controls/configs/:id/buttons/:btnId",
++ "file": "server/controls/routes.js",
++ "line": 1165
++ },
++ {
++ "method": "PUT",
++ "path": "/api/controls/configs/:id/buttons/:btnId",
++ "file": "server/controls/routes.js",
++ "line": 1123
++ },
++ {
++ "method": "POST",
++ "path": "/api/controls/configs/deactivate",
++ "file": "server/controls/routes.js",
++ "line": 1198
++ },
++ {
++ "method": "GET",
++ "path": "/api/controls/cozmo-script",
++ "file": "server/controls/routes.js",
++ "line": 1373
++ },
++ {
++ "method": "GET",
++ "path": "/api/controls/settings/channel",
++ "file": "server/controls/routes.js",
++ "line": 1270
++ },
++ {
++ "method": "PUT",
++ "path": "/api/controls/settings/channel",
++ "file": "server/controls/routes.js",
++ "line": 1287
++ },
++ {
++ "method": "GET",
++ "path": "/api/controls/whitelist",
++ "file": "server/controls/routes.js",
++ "line": 1325
++ },
++ {
++ "method": "POST",
++ "path": "/api/controls/whitelist",
++ "file": "server/controls/routes.js",
++ "line": 1341
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/controls/whitelist/:id",
++ "file": "server/controls/routes.js",
++ "line": 1359
++ },
++ {
++ "method": "POST",
++ "path": "/api/cosmetics/activate",
++ "file": "server/monetization/cosmetics-routes.js",
++ "line": 64
++ },
++ {
++ "method": "GET",
++ "path": "/api/cosmetics/catalog",
++ "file": "server/monetization/cosmetics-routes.js",
++ "line": 20
++ },
++ {
++ "method": "POST",
++ "path": "/api/cosmetics/deactivate",
++ "file": "server/monetization/cosmetics-routes.js",
++ "line": 73
++ },
++ {
++ "method": "POST",
++ "path": "/api/cosmetics/equip",
++ "file": "server/monetization/cosmetics-routes.js",
++ "line": 46
++ },
++ {
++ "method": "GET",
++ "path": "/api/cosmetics/equipped/:userId",
++ "file": "server/monetization/cosmetics-routes.js",
++ "line": 38
++ },
++ {
++ "method": "POST",
++ "path": "/api/cosmetics/internal-unlock",
++ "file": "server/monetization/cosmetics-routes.js",
++ "line": 95
++ },
++ {
++ "method": "GET",
++ "path": "/api/cosmetics/inventory",
++ "file": "server/monetization/cosmetics-routes.js",
++ "line": 32
++ },
++ {
++ "method": "POST",
++ "path": "/api/cosmetics/unequip",
++ "file": "server/monetization/cosmetics-routes.js",
++ "line": 55
++ },
++ {
++ "method": "POST",
++ "path": "/api/csp-report",
++ "file": "server/index.js",
++ "line": 847
++ },
++ {
++ "method": "GET",
++ "path": "/api/dm/blocks",
++ "file": "server/chat/dm-routes.js",
++ "line": 401
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/dm/blocks/:userId",
++ "file": "server/chat/dm-routes.js",
++ "line": 389
++ },
++ {
++ "method": "POST",
++ "path": "/api/dm/blocks/:userId",
++ "file": "server/chat/dm-routes.js",
++ "line": 374
++ },
++ {
++ "method": "GET",
++ "path": "/api/dm/blocks/check/:userId",
++ "file": "server/chat/dm-routes.js",
++ "line": 411
++ },
++ {
++ "method": "GET",
++ "path": "/api/dm/conversations",
++ "file": "server/chat/dm-routes.js",
++ "line": 49
++ },
++ {
++ "method": "POST",
++ "path": "/api/dm/conversations",
++ "file": "server/chat/dm-routes.js",
++ "line": 64
++ },
++ {
++ "method": "GET",
++ "path": "/api/dm/conversations/:id",
++ "file": "server/chat/dm-routes.js",
++ "line": 120
++ },
++ {
++ "method": "PATCH",
++ "path": "/api/dm/conversations/:id",
++ "file": "server/chat/dm-routes.js",
++ "line": 331
++ },
++ {
++ "method": "GET",
++ "path": "/api/dm/conversations/:id/messages",
++ "file": "server/chat/dm-routes.js",
++ "line": 137
++ },
++ {
++ "method": "POST",
++ "path": "/api/dm/conversations/:id/messages",
++ "file": "server/chat/dm-routes.js",
++ "line": 155
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/dm/conversations/:id/messages/:msgId",
++ "file": "server/chat/dm-routes.js",
++ "line": 421
++ },
++ {
++ "method": "POST",
++ "path": "/api/dm/conversations/:id/participants",
++ "file": "server/chat/dm-routes.js",
++ "line": 256
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/dm/conversations/:id/participants/:userId",
++ "file": "server/chat/dm-routes.js",
++ "line": 310
++ },
++ {
++ "method": "POST",
++ "path": "/api/dm/conversations/:id/read",
++ "file": "server/chat/dm-routes.js",
++ "line": 234
++ },
++ {
++ "method": "GET",
++ "path": "/api/dm/unread",
++ "file": "server/chat/dm-routes.js",
++ "line": 350
++ },
++ {
++ "method": "GET",
++ "path": "/api/dm/users/search",
++ "file": "server/chat/dm-routes.js",
++ "line": 360
++ },
++ {
++ "method": "GET",
++ "path": "/api/easter-egg/daily",
++ "file": "server/ai/easter-egg-routes.js",
++ "line": 16
++ },
++ {
++ "method": "POST",
++ "path": "/api/easter-egg/reveal",
++ "file": "server/ai/easter-egg-routes.js",
++ "line": 69
++ },
++ {
++ "method": "POST",
++ "path": "/api/easter-egg/solve",
++ "file": "server/ai/easter-egg-routes.js",
++ "line": 41
++ },
++ {
++ "method": "POST",
++ "path": "/api/emotes",
++ "file": "server/emotes/routes.js",
++ "line": 279
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/emotes/:id",
++ "file": "server/emotes/routes.js",
++ "line": 473
++ },
++ {
++ "method": "PATCH",
++ "path": "/api/emotes/:id",
++ "file": "server/emotes/routes.js",
++ "line": 416
++ },
++ {
++ "method": "GET",
++ "path": "/api/emotes/7tv",
++ "file": "server/emotes/routes.js",
++ "line": 570
++ },
++ {
++ "method": "GET",
++ "path": "/api/emotes/all/:streamId",
++ "file": "server/emotes/routes.js",
++ "line": 660
++ },
++ {
++ "method": "GET",
++ "path": "/api/emotes/bttv",
++ "file": "server/emotes/routes.js",
++ "line": 552
++ },
++ {
++ "method": "GET",
++ "path": "/api/emotes/channel/:userId",
++ "file": "server/emotes/routes.js",
++ "line": 234
++ },
++ {
++ "method": "GET",
++ "path": "/api/emotes/defaults",
++ "file": "server/emotes/routes.js",
++ "line": 588
++ },
++ {
++ "method": "GET",
++ "path": "/api/emotes/ffz",
++ "file": "server/emotes/routes.js",
++ "line": 533
++ },
++ {
++ "method": "GET",
++ "path": "/api/emotes/file/:filename",
++ "file": "server/emotes/routes.js",
++ "line": 505
++ },
++ {
++ "method": "GET",
++ "path": "/api/emotes/global",
++ "file": "server/emotes/routes.js",
++ "line": 215
++ },
++ {
++ "method": "GET",
++ "path": "/api/emotes/mine",
++ "file": "server/emotes/routes.js",
++ "line": 257
++ },
++ {
++ "method": "GET",
++ "path": "/api/emotes/search",
++ "file": "server/emotes/routes.js",
++ "line": 622
++ },
++ {
++ "method": "GET",
++ "path": "/api/emotes/sources",
++ "file": "server/emotes/routes.js",
++ "line": 593
++ },
++ {
++ "method": "PUT",
++ "path": "/api/emotes/sources",
++ "file": "server/emotes/routes.js",
++ "line": 608
++ },
++ {
++ "method": "GET",
++ "path": "/api/funds/balance",
++ "file": "server/monetization/routes.js",
++ "line": 167
++ },
++ {
++ "method": "POST",
++ "path": "/api/funds/cashout",
++ "file": "server/monetization/routes.js",
++ "line": 152
++ },
++ {
++ "method": "POST",
++ "path": "/api/funds/cashout/:id/approve",
++ "file": "server/monetization/routes.js",
++ "line": 263
++ },
++ {
++ "method": "POST",
++ "path": "/api/funds/cashout/:id/deny",
++ "file": "server/monetization/routes.js",
++ "line": 273
++ },
++ {
++ "method": "GET",
++ "path": "/api/funds/cashouts/pending",
++ "file": "server/monetization/routes.js",
++ "line": 283
++ },
++ {
++ "method": "POST",
++ "path": "/api/funds/donate",
++ "file": "server/monetization/routes.js",
++ "line": 53
++ },
++ {
++ "method": "POST",
++ "path": "/api/funds/goals",
++ "file": "server/monetization/routes.js",
++ "line": 209
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/funds/goals/:id",
++ "file": "server/monetization/routes.js",
++ "line": 240
++ },
++ {
++ "method": "PUT",
++ "path": "/api/funds/goals/:id",
++ "file": "server/monetization/routes.js",
++ "line": 223
++ },
++ {
++ "method": "GET",
++ "path": "/api/funds/goals/:userId",
++ "file": "server/monetization/routes.js",
++ "line": 257
++ },
++ {
++ "method": "GET",
++ "path": "/api/funds/goals/manage/mine",
++ "file": "server/monetization/routes.js",
++ "line": 204
++ },
++ {
++ "method": "GET",
++ "path": "/api/funds/history",
++ "file": "server/monetization/routes.js",
++ "line": 191
++ },
++ {
++ "method": "GET",
++ "path": "/api/funds/leaderboard/:streamId",
++ "file": "server/monetization/routes.js",
++ "line": 198
++ },
++ {
++ "method": "POST",
++ "path": "/api/funds/purchase",
++ "file": "server/monetization/routes.js",
++ "line": 34
++ },
++ {
++ "method": "POST",
++ "path": "/api/funds/recycle",
++ "file": "server/monetization/routes.js",
++ "line": 181
++ },
++ {
++ "method": "GET",
++ "path": "/api/health",
++ "file": "server/index.js",
++ "line": 727
++ },
++ {
++ "method": "GET",
++ "path": "/api/home/digest",
++ "file": "server/home/routes.js",
++ "line": 457
++ },
++ {
++ "method": "GET",
++ "path": "/api/home/discover",
++ "file": "server/home/routes.js",
++ "line": 507
++ },
++ {
++ "method": "GET",
++ "path": "/api/home/featured",
++ "file": "server/home/routes.js",
++ "line": 247
++ },
++ {
++ "method": "GET",
++ "path": "/api/home/hero",
++ "file": "server/home/routes.js",
++ "line": 333
++ },
++ {
++ "method": "GET",
++ "path": "/api/home/pulse",
++ "file": "server/home/routes.js",
++ "line": 406
++ },
++ {
++ "method": "GET",
++ "path": "/api/home/star",
++ "file": "server/home/routes.js",
++ "line": 352
++ },
++ {
++ "method": "POST",
++ "path": "/api/home/star/rotate",
++ "file": "server/home/routes.js",
++ "line": 569
++ },
++ {
++ "method": "GET",
++ "path": "/api/home/stats-live",
++ "file": "server/home/routes.js",
++ "line": 287
++ },
++ {
++ "method": "GET",
++ "path": "/api/home/stats/series/:metric",
++ "file": "server/home/routes.js",
++ "line": 177
++ },
++ {
++ "method": "GET",
++ "path": "/api/i18n/languages",
++ "file": "server/i18n/routes.js",
++ "line": 31
++ },
++ {
++ "method": "POST",
++ "path": "/api/i18n/translate",
++ "file": "server/i18n/routes.js",
++ "line": 36
++ },
++ {
++ "method": "GET",
++ "path": "/api/img-proxy",
++ "file": "server/media/external-image-proxy.js",
++ "line": 31
++ },
++ {
++ "method": "GET",
++ "path": "/api/kiosk/favicon",
++ "file": "server/kiosk/routes.js",
++ "line": 122
++ },
++ {
++ "method": "GET",
++ "path": "/api/kiosk/site",
++ "file": "server/kiosk/routes.js",
++ "line": 77
++ },
++ {
++ "method": "GET",
++ "path": "/api/live-events",
++ "file": "server/index.js",
++ "line": 655
++ },
++ {
++ "method": "POST",
++ "path": "/api/media/advance",
++ "file": "server/media/routes.js",
++ "line": 262
++ },
++ {
++ "method": "GET",
++ "path": "/api/media/channel/:username",
++ "file": "server/media/routes.js",
++ "line": 52
++ },
++ {
++ "method": "GET",
++ "path": "/api/media/downloader/status",
++ "file": "server/media/routes.js",
++ "line": 413
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/media/queue/:id",
++ "file": "server/media/routes.js",
++ "line": 309
++ },
++ {
++ "method": "POST",
++ "path": "/api/media/queue/:id/fail",
++ "file": "server/media/routes.js",
++ "line": 398
++ },
++ {
++ "method": "POST",
++ "path": "/api/media/queue/:id/move",
++ "file": "server/media/routes.js",
++ "line": 299
++ },
++ {
++ "method": "POST",
++ "path": "/api/media/queue/:id/play",
++ "file": "server/media/routes.js",
++ "line": 273
++ },
++ {
++ "method": "POST",
++ "path": "/api/media/queue/:id/position",
++ "file": "server/media/routes.js",
++ "line": 365
++ },
++ {
++ "method": "POST",
++ "path": "/api/media/queue/:id/refund",
++ "file": "server/media/routes.js",
++ "line": 383
++ },
++ {
++ "method": "POST",
++ "path": "/api/media/queue/:id/skip",
++ "file": "server/media/routes.js",
++ "line": 290
++ },
++ {
++ "method": "GET",
++ "path": "/api/media/queue/:id/stream-url",
++ "file": "server/media/routes.js",
++ "line": 320
++ },
++ {
++ "method": "POST",
++ "path": "/api/media/quote",
++ "file": "server/media/routes.js",
++ "line": 165
++ },
++ {
++ "method": "POST",
++ "path": "/api/media/request",
++ "file": "server/media/routes.js",
++ "line": 217
++ },
++ {
++ "method": "GET",
++ "path": "/api/media/settings",
++ "file": "server/media/routes.js",
++ "line": 98
++ },
++ {
++ "method": "PUT",
++ "path": "/api/media/settings",
++ "file": "server/media/routes.js",
++ "line": 102
++ },
++ {
++ "method": "POST",
++ "path": "/api/media/start",
++ "file": "server/media/routes.js",
++ "line": 253
++ },
++ {
++ "method": "GET",
++ "path": "/api/meta/openvibeapp",
++ "file": "server/meta/routes.js",
++ "line": 87
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/mod/ban/:id",
++ "file": "server/admin/mod-routes.js",
++ "line": 269
++ },
++ {
++ "method": "GET",
++ "path": "/api/mod/bans",
++ "file": "server/admin/mod-routes.js",
++ "line": 42
++ },
++ {
++ "method": "GET",
++ "path": "/api/mod/chat/search",
++ "file": "server/admin/mod-routes.js",
++ "line": 315
++ },
++ {
++ "method": "GET",
++ "path": "/api/mod/chat/user/:userId",
++ "file": "server/admin/mod-routes.js",
++ "line": 346
++ },
++ {
++ "method": "POST",
++ "path": "/api/mod/delete-message",
++ "file": "server/admin/mod-routes.js",
++ "line": 364
++ },
++ {
++ "method": "POST",
++ "path": "/api/mod/delete-user-messages",
++ "file": "server/admin/mod-routes.js",
++ "line": 419
++ },
++ {
++ "method": "POST",
++ "path": "/api/mod/global-ban",
++ "file": "server/admin/mod-routes.js",
++ "line": 146
++ },
++ {
++ "method": "POST",
++ "path": "/api/mod/ip-approval/:channelId/approve",
++ "file": "server/admin/mod-routes.js",
++ "line": 611
++ },
++ {
++ "method": "POST",
++ "path": "/api/mod/ip-approval/:channelId/deny",
++ "file": "server/admin/mod-routes.js",
++ "line": 668
++ },
++ {
++ "method": "GET",
++ "path": "/api/mod/ip-approval/:channelId/pending",
++ "file": "server/admin/mod-routes.js",
++ "line": 584
++ },
++ {
++ "method": "POST",
++ "path": "/api/mod/ip-approval/:channelId/review",
++ "file": "server/admin/mod-routes.js",
++ "line": 698
++ },
++ {
++ "method": "GET",
++ "path": "/api/mod/ip/alts/:userId",
++ "file": "server/admin/mod-routes.js",
++ "line": 801
++ },
++ {
++ "method": "GET",
++ "path": "/api/mod/ip/anon/:anonId",
++ "file": "server/admin/mod-routes.js",
++ "line": 755
++ },
++ {
++ "method": "POST",
++ "path": "/api/mod/ip/ban-all",
++ "file": "server/admin/mod-routes.js",
++ "line": 813
++ },
++ {
++ "method": "GET",
++ "path": "/api/mod/ip/log",
++ "file": "server/admin/mod-routes.js",
++ "line": 853
++ },
++ {
++ "method": "GET",
++ "path": "/api/mod/ip/lookup/:ip",
++ "file": "server/admin/mod-routes.js",
++ "line": 786
++ },
++ {
++ "method": "GET",
++ "path": "/api/mod/ip/user/:userId",
++ "file": "server/admin/mod-routes.js",
++ "line": 730
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/mod/relay-user/:id",
++ "file": "server/admin/mod-routes.js",
++ "line": 542
++ },
++ {
++ "method": "POST",
++ "path": "/api/mod/relay-user/hide",
++ "file": "server/admin/mod-routes.js",
++ "line": 484
++ },
++ {
++ "method": "GET",
++ "path": "/api/mod/relay-users/hidden/:channelId",
++ "file": "server/admin/mod-routes.js",
++ "line": 567
++ },
++ {
++ "method": "POST",
++ "path": "/api/mod/stream-ban",
++ "file": "server/admin/mod-routes.js",
++ "line": 197
++ },
++ {
++ "method": "GET",
++ "path": "/api/mod/tts-voice/:kind/:id",
++ "file": "server/admin/mod-routes.js",
++ "line": 889
++ },
++ {
++ "method": "PUT",
++ "path": "/api/mod/tts-voice/:kind/:id",
++ "file": "server/admin/mod-routes.js",
++ "line": 910
++ },
++ {
++ "method": "POST",
++ "path": "/api/mod/tts-voice/preview",
++ "file": "server/admin/mod-routes.js",
++ "line": 940
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/mod/users/:id/ban",
++ "file": "server/admin/mod-routes.js",
++ "line": 169
++ },
++ {
++ "method": "POST",
++ "path": "/api/mod/users/:id/ban",
++ "file": "server/admin/mod-routes.js",
++ "line": 159
++ },
++ {
++ "method": "GET",
++ "path": "/api/news/my-settings",
++ "file": "server/news/news-routes.js",
++ "line": 71
++ },
++ {
++ "method": "PUT",
++ "path": "/api/news/my-settings",
++ "file": "server/news/news-routes.js",
++ "line": 77
++ },
++ {
++ "method": "GET",
++ "path": "/api/news/sources",
++ "file": "server/news/news-routes.js",
++ "line": 35
++ },
++ {
++ "method": "PUT",
++ "path": "/api/news/sources/:id",
++ "file": "server/news/news-routes.js",
++ "line": 43
++ },
++ {
++ "method": "GET",
++ "path": "/api/onvif/cameras",
++ "file": "server/controls/onvif-routes.js",
++ "line": 182
++ },
++ {
++ "method": "POST",
++ "path": "/api/onvif/cameras",
++ "file": "server/controls/onvif-routes.js",
++ "line": 105
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/onvif/cameras/:cameraId/presets/:presetId",
++ "file": "server/controls/onvif-routes.js",
++ "line": 419
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/onvif/cameras/:id",
++ "file": "server/controls/onvif-routes.js",
++ "line": 324
++ },
++ {
++ "method": "GET",
++ "path": "/api/onvif/cameras/:id",
++ "file": "server/controls/onvif-routes.js",
++ "line": 210
++ },
++ {
++ "method": "PUT",
++ "path": "/api/onvif/cameras/:id",
++ "file": "server/controls/onvif-routes.js",
++ "line": 263
++ },
++ {
++ "method": "GET",
++ "path": "/api/onvif/cameras/:id/presets",
++ "file": "server/controls/onvif-routes.js",
++ "line": 349
++ },
++ {
++ "method": "POST",
++ "path": "/api/onvif/cameras/:id/presets",
++ "file": "server/controls/onvif-routes.js",
++ "line": 372
++ },
++ {
++ "method": "POST",
++ "path": "/api/onvif/discover",
++ "file": "server/controls/onvif-routes.js",
++ "line": 70
++ },
++ {
++ "method": "GET",
++ "path": "/api/pastes",
++ "file": "server/media-proxy/pastes.js",
++ "line": 167
++ },
++ {
++ "method": "POST",
++ "path": "/api/pastes",
++ "file": "server/media-proxy/pastes.js",
++ "line": 168
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/pastes/:slug",
++ "file": "server/media-proxy/pastes.js",
++ "line": 217
++ },
++ {
++ "method": "GET",
++ "path": "/api/pastes/:slug",
++ "file": "server/media-proxy/pastes.js",
++ "line": 215
++ },
++ {
++ "method": "PUT",
++ "path": "/api/pastes/:slug",
++ "file": "server/media-proxy/pastes.js",
++ "line": 216
++ },
++ {
++ "method": "POST",
++ "path": "/api/pastes/:slug/censor",
++ "file": "server/media-proxy/pastes.js",
++ "line": 222
++ },
++ {
++ "method": "GET",
++ "path": "/api/pastes/:slug/comments",
++ "file": "server/media-proxy/pastes.js",
++ "line": 243
++ },
++ {
++ "method": "POST",
++ "path": "/api/pastes/:slug/comments",
++ "file": "server/media-proxy/pastes.js",
++ "line": 246
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/pastes/:slug/comments/:commentId",
++ "file": "server/media-proxy/pastes.js",
++ "line": 248
++ },
++ {
++ "method": "POST",
++ "path": "/api/pastes/:slug/copy",
++ "file": "server/media-proxy/pastes.js",
++ "line": 242
++ },
++ {
++ "method": "POST",
++ "path": "/api/pastes/:slug/fork",
++ "file": "server/media-proxy/pastes.js",
++ "line": 238
++ },
++ {
++ "method": "POST",
++ "path": "/api/pastes/:slug/like",
++ "file": "server/media-proxy/pastes.js",
++ "line": 241
++ },
++ {
++ "method": "GET",
++ "path": "/api/pastes/:slug/raw",
++ "file": "server/media-proxy/pastes.js",
++ "line": 240
++ },
++ {
++ "method": "POST",
++ "path": "/api/pastes/:slug/set-avatar",
++ "file": "server/media-proxy/pastes.js",
++ "line": 110
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/pastes/admin/forks",
++ "file": "server/media-proxy/pastes.js",
++ "line": 171
++ },
++ {
++ "method": "GET",
++ "path": "/api/pastes/admin/stats",
++ "file": "server/media-proxy/pastes.js",
++ "line": 170
++ },
++ {
++ "method": "POST",
++ "path": "/api/pastes/bulk",
++ "file": "server/media-proxy/pastes.js",
++ "line": 172
++ },
++ {
++ "method": "GET",
++ "path": "/api/pastes/by-user/:username",
++ "file": "server/media-proxy/pastes.js",
++ "line": 177
++ },
++ {
++ "method": "GET",
++ "path": "/api/pastes/config",
++ "file": "server/media-proxy/pastes.js",
++ "line": 169
++ },
++ {
++ "method": "POST",
++ "path": "/api/pastes/screenshot",
++ "file": "server/media-proxy/pastes.js",
++ "line": 83
++ },
++ {
++ "method": "POST",
++ "path": "/api/payments/bucks/checkout",
++ "file": "server/monetization/payments-routes.js",
++ "line": 24
++ },
++ {
++ "method": "GET",
++ "path": "/api/payments/channel/:username",
++ "file": "server/monetization/payments-routes.js",
++ "line": 192
++ },
++ {
++ "method": "GET",
++ "path": "/api/payments/config",
++ "file": "server/monetization/payments-routes.js",
++ "line": 19
++ },
++ {
++ "method": "GET",
++ "path": "/api/payments/paypal/return",
++ "file": "server/monetization/payments-routes.js",
++ "line": 82
++ },
++ {
++ "method": "POST",
++ "path": "/api/payments/subscribe",
++ "file": "server/monetization/payments-routes.js",
++ "line": 102
++ },
++ {
++ "method": "POST",
++ "path": "/api/payments/subscriptions/:id/cancel",
++ "file": "server/monetization/payments-routes.js",
++ "line": 208
++ },
++ {
++ "method": "GET",
++ "path": "/api/payments/subscriptions/mine",
++ "file": "server/monetization/payments-routes.js",
++ "line": 187
++ },
++ {
++ "method": "ALL",
++ "path": "/api/payments/webhook/ccbill",
++ "file": "server/monetization/payments-routes.js",
++ "line": 280
++ },
++ {
++ "method": "POST",
++ "path": "/api/payments/webhook/crypto",
++ "file": "server/monetization/payments-routes.js",
++ "line": 299
++ },
++ {
++ "method": "POST",
++ "path": "/api/payments/webhook/paypal",
++ "file": "server/monetization/payments-routes.js",
++ "line": 263
++ },
++ {
++ "method": "POST",
++ "path": "/api/payments/webhook/stripe",
++ "file": "server/monetization/payments-routes.js",
++ "line": 233
++ },
++ {
++ "method": "GET",
++ "path": "/api/powerchat/authorize-url",
++ "file": "server/integrations/powerchat-routes.js",
++ "line": 436
++ },
++ {
++ "method": "GET",
++ "path": "/api/powerchat/donate-link",
++ "file": "server/integrations/powerchat-routes.js",
++ "line": 313
++ },
++ {
++ "method": "GET",
++ "path": "/api/powerchat/oauth/callback",
++ "file": "server/integrations/powerchat-routes.js",
++ "line": 199
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/powerchat/oauth/connection",
++ "file": "server/integrations/powerchat-routes.js",
++ "line": 275
++ },
++ {
++ "method": "GET",
++ "path": "/api/powerchat/oauth/start",
++ "file": "server/integrations/powerchat-routes.js",
++ "line": 182
++ },
++ {
++ "method": "POST",
++ "path": "/api/powerchat/reconcile",
++ "file": "server/integrations/powerchat-routes.js",
++ "line": 339
++ },
++ {
++ "method": "GET",
++ "path": "/api/powerchat/status",
++ "file": "server/integrations/powerchat-routes.js",
++ "line": 86
++ },
++ {
++ "method": "POST",
++ "path": "/api/powerchat/test-alert",
++ "file": "server/integrations/powerchat-routes.js",
++ "line": 389
++ },
++ {
++ "method": "POST",
++ "path": "/api/powerchat/test-tip",
++ "file": "server/integrations/powerchat-routes.js",
++ "line": 350
++ },
++ {
++ "method": "GET",
++ "path": "/api/powerchat/tip-link",
++ "file": "server/integrations/powerchat-routes.js",
++ "line": 291
++ },
++ {
++ "method": "POST",
++ "path": "/api/powerchat/webhook",
++ "file": "server/integrations/powerchat-routes.js",
++ "line": 442
++ },
++ {
++ "method": "GET",
++ "path": "/api/ready",
++ "file": "server/index.js",
++ "line": 750
++ },
++ {
++ "method": "GET",
++ "path": "/api/recap/:streamId",
++ "file": "server/recap/routes.js",
++ "line": 21
++ },
++ {
++ "method": "POST",
++ "path": "/api/recap/:streamId/regenerate",
++ "file": "server/recap/routes.js",
++ "line": 43
++ },
++ {
++ "method": "GET",
++ "path": "/api/recap/channel/:username",
++ "file": "server/recap/routes.js",
++ "line": 14
++ },
++ {
++ "method": "GET",
++ "path": "/api/restream/destinations",
++ "file": "server/streaming/restream-routes.js",
++ "line": 157
++ },
++ {
++ "method": "POST",
++ "path": "/api/restream/destinations",
++ "file": "server/streaming/restream-routes.js",
++ "line": 179
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/restream/destinations/:id",
++ "file": "server/streaming/restream-routes.js",
++ "line": 341
++ },
++ {
++ "method": "PUT",
++ "path": "/api/restream/destinations/:id",
++ "file": "server/streaming/restream-routes.js",
++ "line": 253
++ },
++ {
++ "method": "POST",
++ "path": "/api/restream/destinations/:id/start",
++ "file": "server/streaming/restream-routes.js",
++ "line": 364
++ },
++ {
++ "method": "POST",
++ "path": "/api/restream/destinations/:id/stop",
++ "file": "server/streaming/restream-routes.js",
++ "line": 420
++ },
++ {
++ "method": "GET",
++ "path": "/api/restream/oauth/:platform/callback",
++ "file": "server/streaming/restream-routes.js",
++ "line": 599
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/restream/oauth/:platform/connection",
++ "file": "server/streaming/restream-routes.js",
++ "line": 693
++ },
++ {
++ "method": "GET",
++ "path": "/api/restream/oauth/:platform/start",
++ "file": "server/streaming/restream-routes.js",
++ "line": 585
++ },
++ {
++ "method": "GET",
++ "path": "/api/restream/oauth/status",
++ "file": "server/streaming/restream-routes.js",
++ "line": 563
++ },
++ {
++ "method": "GET",
++ "path": "/api/restream/presets",
++ "file": "server/streaming/restream-routes.js",
++ "line": 147
++ },
++ {
++ "method": "GET",
++ "path": "/api/restream/status",
++ "file": "server/streaming/restream-routes.js",
++ "line": 440
++ },
++ {
++ "method": "GET",
++ "path": "/api/restream/viewer-config",
++ "file": "server/streaming/restream-routes.js",
++ "line": 489
++ },
++ {
++ "method": "GET",
++ "path": "/api/restream/viewer-counts",
++ "file": "server/streaming/restream-routes.js",
++ "line": 479
++ },
++ {
++ "method": "POST",
++ "path": "/api/restream/viewer-counts",
++ "file": "server/streaming/restream-routes.js",
++ "line": 461
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/robotstreamer/integration",
++ "file": "server/integrations/routes.js",
++ "line": 226
++ },
++ {
++ "method": "GET",
++ "path": "/api/robotstreamer/integration",
++ "file": "server/integrations/routes.js",
++ "line": 41
++ },
++ {
++ "method": "PUT",
++ "path": "/api/robotstreamer/integration",
++ "file": "server/integrations/routes.js",
++ "line": 152
++ },
++ {
++ "method": "POST",
++ "path": "/api/robotstreamer/integration/login",
++ "file": "server/integrations/routes.js",
++ "line": 107
++ },
++ {
++ "method": "POST",
++ "path": "/api/robotstreamer/integration/validate",
++ "file": "server/integrations/routes.js",
++ "line": 70
++ },
++ {
++ "method": "POST",
++ "path": "/api/robotstreamer/restream/start",
++ "file": "server/integrations/routes.js",
++ "line": 194
++ },
++ {
++ "method": "POST",
++ "path": "/api/robotstreamer/restream/stop",
++ "file": "server/integrations/routes.js",
++ "line": 214
++ },
++ {
++ "method": "POST",
++ "path": "/api/sounds",
++ "file": "server/chat/sounds-routes.js",
++ "line": 147
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/sounds/:id",
++ "file": "server/chat/sounds-routes.js",
++ "line": 287
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/sounds/alert/:kind",
++ "file": "server/chat/sounds-routes.js",
++ "line": 440
++ },
++ {
++ "method": "POST",
++ "path": "/api/sounds/alert/:kind",
++ "file": "server/chat/sounds-routes.js",
++ "line": 403
++ },
++ {
++ "method": "GET",
++ "path": "/api/sounds/alert/mine",
++ "file": "server/chat/sounds-routes.js",
++ "line": 393
++ },
++ {
++ "method": "GET",
++ "path": "/api/sounds/all/:streamId",
++ "file": "server/chat/sounds-routes.js",
++ "line": 136
++ },
++ {
++ "method": "GET",
++ "path": "/api/sounds/channel/:userId",
++ "file": "server/chat/sounds-routes.js",
++ "line": 125
++ },
++ {
++ "method": "PATCH",
++ "path": "/api/sounds/command",
++ "file": "server/chat/sounds-routes.js",
++ "line": 313
++ },
++ {
++ "method": "GET",
++ "path": "/api/sounds/file/:filename",
++ "file": "server/chat/sounds-routes.js",
++ "line": 371
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams",
++ "file": "server/streaming/routes.js",
++ "line": 1084
++ },
++ {
++ "method": "POST",
++ "path": "/api/streams",
++ "file": "server/streaming/routes.js",
++ "line": 1886
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/streams/:id",
++ "file": "server/streaming/routes.js",
++ "line": 2081
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/:id",
++ "file": "server/streaming/routes.js",
++ "line": 1838
++ },
++ {
++ "method": "PUT",
++ "path": "/api/streams/:id",
++ "file": "server/streaming/routes.js",
++ "line": 2030
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/:id/call",
++ "file": "server/streaming/routes.js",
++ "line": 2384
++ },
++ {
++ "method": "PUT",
++ "path": "/api/streams/:id/call",
++ "file": "server/streaming/routes.js",
++ "line": 2345
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/:id/endpoint",
++ "file": "server/streaming/routes.js",
++ "line": 2128
++ },
++ {
++ "method": "POST",
++ "path": "/api/streams/:id/follow",
++ "file": "server/streaming/routes.js",
++ "line": 2253
++ },
++ {
++ "method": "POST",
++ "path": "/api/streams/:id/heartbeat",
++ "file": "server/streaming/routes.js",
++ "line": 2214
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/:id/rtmp-status",
++ "file": "server/streaming/routes.js",
++ "line": 2232
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/broadcast-settings",
++ "file": "server/streaming/routes.js",
++ "line": 1383
++ },
++ {
++ "method": "PUT",
++ "path": "/api/streams/broadcast-settings",
++ "file": "server/streaming/routes.js",
++ "line": 1400
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/channel",
++ "file": "server/streaming/routes.js",
++ "line": 636
++ },
++ {
++ "method": "PUT",
++ "path": "/api/streams/channel",
++ "file": "server/streaming/routes.js",
++ "line": 648
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/channel/:username",
++ "file": "server/streaming/routes.js",
++ "line": 205
++ },
++ {
++ "method": "PUT",
++ "path": "/api/streams/channel/:username/about",
++ "file": "server/streaming/routes.js",
++ "line": 763
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/channel/:username/bio-en",
++ "file": "server/streaming/routes.js",
++ "line": 744
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/channel/:username/clips-taken",
++ "file": "server/streaming/routes.js",
++ "line": 555
++ },
++ {
++ "method": "POST",
++ "path": "/api/streams/channel/:username/follow",
++ "file": "server/streaming/routes.js",
++ "line": 2298
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/channel/:username/live",
++ "file": "server/streaming/routes.js",
++ "line": 598
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/channel/:username/popular",
++ "file": "server/streaming/routes.js",
++ "line": 529
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/channel/:username/resolve/:ref",
++ "file": "server/streaming/routes.js",
++ "line": 1422
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/channel/:username/weather",
++ "file": "server/streaming/routes.js",
++ "line": 973
++ },
++ {
++ "method": "POST",
++ "path": "/api/streams/channel/offline-screen",
++ "file": "server/streaming/routes.js",
++ "line": 869
++ },
++ {
++ "method": "POST",
++ "path": "/api/streams/diag-log",
++ "file": "server/streaming/routes.js",
++ "line": 2207
++ },
++ {
++ "method": "POST",
++ "path": "/api/streams/goal-media",
++ "file": "server/streaming/routes.js",
++ "line": 834
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/managed",
++ "file": "server/streaming/routes.js",
++ "line": 1567
++ },
++ {
++ "method": "POST",
++ "path": "/api/streams/managed",
++ "file": "server/streaming/routes.js",
++ "line": 1579
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/streams/managed/:id",
++ "file": "server/streaming/routes.js",
++ "line": 1793
++ },
++ {
++ "method": "PUT",
++ "path": "/api/streams/managed/:id",
++ "file": "server/streaming/routes.js",
++ "line": 1633
++ },
++ {
++ "method": "POST",
++ "path": "/api/streams/managed/:id/regenerate-key",
++ "file": "server/streaming/routes.js",
++ "line": 1818
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/managed/:managedStreamId/history",
++ "file": "server/streaming/routes.js",
++ "line": 1456
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/managed/:managedStreamId/profile",
++ "file": "server/streaming/routes.js",
++ "line": 1470
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/mine",
++ "file": "server/streaming/routes.js",
++ "line": 1120
++ },
++ {
++ "method": "POST",
++ "path": "/api/streams/panel-image",
++ "file": "server/streaming/routes.js",
++ "line": 812
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/recent",
++ "file": "server/streaming/routes.js",
++ "line": 1132
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/recent-vods",
++ "file": "server/streaming/routes.js",
++ "line": 1202
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/recently-online",
++ "file": "server/streaming/routes.js",
++ "line": 1148
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/rtmp-proxy/:streamId.flv",
++ "file": "server/streaming/routes.js",
++ "line": 2405
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/setup-progress",
++ "file": "server/streaming/routes.js",
++ "line": 1513
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/voice-channels",
++ "file": "server/streaming/routes.js",
++ "line": 1232
++ },
++ {
++ "method": "POST",
++ "path": "/api/streams/voice-channels",
++ "file": "server/streaming/routes.js",
++ "line": 1253
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/streams/voice-channels/:channelId",
++ "file": "server/streaming/routes.js",
++ "line": 1265
++ },
++ {
++ "method": "GET",
++ "path": "/api/streams/voice-channels/:channelId",
++ "file": "server/streaming/routes.js",
++ "line": 1242
++ },
++ {
++ "method": "POST",
++ "path": "/api/streams/voice-channels/call-user",
++ "file": "server/streaming/routes.js",
++ "line": 1277
++ },
++ {
++ "method": "POST",
++ "path": "/api/streams/voice-channels/call-user/respond",
++ "file": "server/streaming/routes.js",
++ "line": 1346
++ },
++ {
++ "method": "GET",
++ "path": "/api/themes",
++ "file": "server/themes/routes.js",
++ "line": 20
++ },
++ {
++ "method": "POST",
++ "path": "/api/themes",
++ "file": "server/themes/routes.js",
++ "line": 108
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/themes/:id",
++ "file": "server/themes/routes.js",
++ "line": 147
++ },
++ {
++ "method": "PUT",
++ "path": "/api/themes/:id",
++ "file": "server/themes/routes.js",
++ "line": 134
++ },
++ {
++ "method": "POST",
++ "path": "/api/themes/:id/download",
++ "file": "server/themes/routes.js",
++ "line": 158
++ },
++ {
++ "method": "GET",
++ "path": "/api/themes/:idOrSlug",
++ "file": "server/themes/routes.js",
++ "line": 88
++ },
++ {
++ "method": "GET",
++ "path": "/api/themes/me",
++ "file": "server/themes/routes.js",
++ "line": 46
++ },
++ {
++ "method": "PUT",
++ "path": "/api/themes/me",
++ "file": "server/themes/routes.js",
++ "line": 73
++ },
++ {
++ "method": "GET",
++ "path": "/api/thumbnails/:filename",
++ "file": "server/media-proxy/thumbnails.js",
++ "line": 84
++ },
++ {
++ "method": "GET",
++ "path": "/api/thumbnails/generate/clip/:id",
++ "file": "server/media-proxy/thumbnails.js",
++ "line": 80
++ },
++ {
++ "method": "POST",
++ "path": "/api/thumbnails/generate/clip/:id",
++ "file": "server/media-proxy/thumbnails.js",
++ "line": 67
++ },
++ {
++ "method": "GET",
++ "path": "/api/thumbnails/generate/vod/:id",
++ "file": "server/media-proxy/thumbnails.js",
++ "line": 79
++ },
++ {
++ "method": "POST",
++ "path": "/api/thumbnails/generate/vod/:id",
++ "file": "server/media-proxy/thumbnails.js",
++ "line": 66
++ },
++ {
++ "method": "POST",
++ "path": "/api/thumbnails/live/:streamId",
++ "file": "server/media-proxy/thumbnails.js",
++ "line": 29
++ },
++ {
++ "method": "GET",
++ "path": "/api/tts/admin/settings",
++ "file": "server/chat/tts-routes.js",
++ "line": 64
++ },
++ {
++ "method": "PUT",
++ "path": "/api/tts/admin/settings",
++ "file": "server/chat/tts-routes.js",
++ "line": 81
++ },
++ {
++ "method": "POST",
++ "path": "/api/tts/admin/test",
++ "file": "server/chat/tts-routes.js",
++ "line": 110
++ },
++ {
++ "method": "GET",
++ "path": "/api/tts/audio/:file",
++ "file": "server/chat/tts-routes.js",
++ "line": 125
++ },
++ {
++ "method": "GET",
++ "path": "/api/tts/settings",
++ "file": "server/chat/tts-routes.js",
++ "line": 43
++ },
++ {
++ "method": "GET",
++ "path": "/api/tts/voices",
++ "file": "server/chat/tts-routes.js",
++ "line": 33
++ },
++ {
++ "method": "GET",
++ "path": "/api/updates",
++ "file": "server/index.js",
++ "line": 813
++ },
++ {
++ "method": "GET",
++ "path": "/api/vibe-coding/channel/:username/:slotIdOrSlug/events",
++ "file": "server/vibe-coding/routes.js",
++ "line": 17
++ },
++ {
++ "method": "GET",
++ "path": "/api/vibe-coding/managed/:managedStreamId/events",
++ "file": "server/vibe-coding/routes.js",
++ "line": 47
++ },
++ {
++ "method": "GET",
++ "path": "/api/vibe-coding/managed/:managedStreamId/settings",
++ "file": "server/vibe-coding/routes.js",
++ "line": 74
++ },
++ {
++ "method": "PUT",
++ "path": "/api/vibe-coding/managed/:managedStreamId/settings",
++ "file": "server/vibe-coding/routes.js",
++ "line": 91
++ },
++ {
++ "method": "GET",
++ "path": "/api/vods",
++ "file": "server/media-proxy/vods.js",
++ "line": 270
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/vods/:id",
++ "file": "server/media-proxy/vods.js",
++ "line": 545
++ },
++ {
++ "method": "GET",
++ "path": "/api/vods/:id",
++ "file": "server/media-proxy/vods.js",
++ "line": 429
++ },
++ {
++ "method": "PUT",
++ "path": "/api/vods/:id",
++ "file": "server/media-proxy/vods.js",
++ "line": 500
++ },
++ {
++ "method": "GET",
++ "path": "/api/vods/:id/context",
++ "file": "server/media-proxy/vods.js",
++ "line": 368
++ },
++ {
++ "method": "GET",
++ "path": "/api/vods/:id/live-info",
++ "file": "server/media-proxy/vods.js",
++ "line": 248
++ },
++ {
++ "method": "GET",
++ "path": "/api/vods/:id/memories",
++ "file": "server/media-proxy/vods.js",
++ "line": 344
++ },
++ {
++ "method": "POST",
++ "path": "/api/vods/:id/publish",
++ "file": "server/media-proxy/vods.js",
++ "line": 556
++ },
++ {
++ "method": "POST",
++ "path": "/api/vods/bulk",
++ "file": "server/media-proxy/vods.js",
++ "line": 517
++ },
++ {
++ "method": "POST",
++ "path": "/api/vods/bulk-delete-old",
++ "file": "server/media-proxy/vods.js",
++ "line": 300
++ },
++ {
++ "method": "POST",
++ "path": "/api/vods/clips",
++ "file": "server/media-proxy/vods.js",
++ "line": 616
++ },
++ {
++ "method": "POST",
++ "path": "/api/vods/clips/:id/trim",
++ "file": "server/media-proxy/vods.js",
++ "line": 771
++ },
++ {
++ "method": "GET",
++ "path": "/api/vods/clips/stream/:streamId",
++ "file": "server/media-proxy/vods.js",
++ "line": 760
++ },
++ {
++ "method": "GET",
++ "path": "/api/vods/file/:filename",
++ "file": "server/media-proxy/vods.js",
++ "line": 572
++ },
++ {
++ "method": "GET",
++ "path": "/api/vods/mine",
++ "file": "server/media-proxy/vods.js",
++ "line": 288
++ },
++ {
++ "method": "POST",
++ "path": "/api/vods/stream/:streamId/chunk",
++ "file": "server/media-proxy/vods.js",
++ "line": 154
++ },
++ {
++ "method": "POST",
++ "path": "/api/vods/stream/:streamId/finalize",
++ "file": "server/media-proxy/vods.js",
++ "line": 208
++ },
++ {
++ "method": "GET",
++ "path": "/api/vods/stream/:streamId/live",
++ "file": "server/media-proxy/vods.js",
++ "line": 228
++ },
++ {
++ "method": "POST",
++ "path": "/api/vods/upload",
++ "file": "server/media-proxy/vods.js",
++ "line": 578
++ },
++ {
++ "method": "GET",
++ "path": "/banned",
++ "file": "server/index.js",
++ "line": 981
++ },
++ {
++ "method": "POST",
++ "path": "/banned/continue",
++ "file": "server/index.js",
++ "line": 951
++ },
++ {
++ "method": "GET",
++ "path": "/canvas",
++ "file": "server/index.js",
++ "line": 683
++ },
++ {
++ "method": "GET",
++ "path": "/data/pastes/screenshots/:filename",
++ "file": "server/index.js",
++ "line": 606
++ },
++ {
++ "method": "GET",
++ "path": "/dmca",
++ "file": "server/index.js",
++ "line": 890
++ },
++ {
++ "method": "GET",
++ "path": "/docs",
++ "file": "server/docs/routes.js",
++ "line": 304
++ },
++ {
++ "method": "GET",
++ "path": "/docs/:name",
++ "file": "server/docs/routes.js",
++ "line": 306
++ },
++ {
++ "method": "GET",
++ "path": "/docs/:name.md",
++ "file": "server/docs/routes.js",
++ "line": 305
++ },
++ {
++ "method": "GET",
++ "path": "/game",
++ "file": "server/index.js",
++ "line": 682
++ },
++ {
++ "method": "POST",
++ "path": "/internal/ai/site-copy",
++ "file": "server/internal/routes.js",
++ "line": 25
++ },
++ {
++ "method": "GET",
++ "path": "/internal/analytics-summary",
++ "file": "server/index.js",
++ "line": 578
++ },
++ {
++ "method": "POST",
++ "path": "/internal/media-webhook",
++ "file": "server/index.js",
++ "line": 573
++ },
++ {
++ "method": "POST",
++ "path": "/internal/url-registry/refresh",
++ "file": "server/internal/routes.js",
++ "line": 49
++ },
++ {
++ "method": "POST",
++ "path": "/internal/user-avatar",
++ "file": "server/internal/routes.js",
++ "line": 67
++ },
++ {
++ "method": "POST",
++ "path": "/internal/user-role",
++ "file": "server/internal/routes.js",
++ "line": 91
++ },
++ {
++ "method": "GET",
++ "path": "/media/:username",
++ "file": "server/index.js",
++ "line": 885
++ },
++ {
++ "method": "GET",
++ "path": "/obs/chat/:username",
++ "file": "server/index.js",
++ "line": 873
++ },
++ {
++ "method": "GET",
++ "path": "/openvibe-sw.js",
++ "file": "server/index.js",
++ "line": 474
++ },
++ {
++ "method": "GET",
++ "path": "/overlay/chat/:username",
++ "file": "server/index.js",
++ "line": 881
++ },
++ {
++ "method": "GET",
++ "path": "/overlay/chat/:username/:slotIdOrSlug",
++ "file": "server/index.js",
++ "line": 878
++ },
++ {
++ "method": "GET",
++ "path": "/p/:slug",
++ "file": "server/index.js",
++ "line": 624
++ },
++ {
++ "method": "GET",
++ "path": "/p/:slug/raw",
++ "file": "server/index.js",
++ "line": 635
++ },
++ {
++ "method": "GET",
++ "path": "/p/:slug/screenshot",
++ "file": "server/index.js",
++ "line": 631
++ },
++ {
++ "method": "GET",
++ "path": "/pastes",
++ "file": "server/index.js",
++ "line": 625
++ },
++ {
++ "method": "GET",
++ "path": "/privacy",
++ "file": "server/index.js",
++ "line": 899
++ },
++ {
++ "method": "GET",
++ "path": "/sitemap.xml",
++ "file": "server/seo/seo.js",
++ "line": 594
++ },
++ {
++ "method": "GET",
++ "path": "/terms",
++ "file": "server/index.js",
++ "line": 896
++ },
++ {
++ "method": "GET",
++ "path": "/tos",
++ "file": "server/index.js",
++ "line": 893
++ },
++ {
++ "method": "POST",
++ "path": "/whip/:streamId",
++ "file": "server/index.js",
++ "line": 908
++ },
++ {
++ "method": "DELETE",
++ "path": "/whip/:streamId/:resourceId",
++ "file": "server/index.js",
++ "line": 910
++ },
++ {
++ "method": "PATCH",
++ "path": "/whip/:streamId/:resourceId",
++ "file": "server/index.js",
++ "line": 909
++ }
++ ],
++ "ws": {
++ "servers": [
++ {
++ "file": "server/chat/chat-server.js",
++ "line": 244
++ },
++ {
++ "file": "server/controls/control-server.js",
++ "line": 32
++ },
++ {
++ "file": "server/integrations/robotstreamer-service.js",
++ "line": 42
++ },
++ {
++ "file": "server/streaming/broadcast-server.js",
++ "line": 130
++ },
++ {
++ "file": "server/streaming/call-server.js",
++ "line": 62
++ },
++ {
++ "file": "server/streaming/jsmpeg-relay.js",
++ "line": 53
++ },
++ {
++ "file": "server/streaming/jsmpeg-relay.js",
++ "line": 99
++ },
++ {
++ "file": "server/vibe-coding/publish-server.js",
++ "line": 23
++ }
++ ],
++ "paths": [
++ {
++ "path": "/ws/chat",
++ "file": "server/chat/chat-server.js",
++ "line": 294
++ },
++ {
++ "path": "/ws/control",
++ "file": "server/controls/control-server.js",
++ "line": 46
++ },
++ {
++ "path": "/ws/",
++ "file": "server/index.js",
++ "line": 418
++ },
++ {
++ "path": "/ws/",
++ "file": "server/index.js",
++ "line": 428
++ },
++ {
++ "path": "/ws/",
++ "file": "server/index.js",
++ "line": 1002
++ },
++ {
++ "path": "/ws/chat",
++ "file": "server/index.js",
++ "line": 1040
++ },
++ {
++ "path": "/ws/vibe-coding/publish",
++ "file": "server/index.js",
++ "line": 1042
++ },
++ {
++ "path": "/ws/broadcast",
++ "file": "server/index.js",
++ "line": 1044
++ },
++ {
++ "path": "/ws/control",
++ "file": "server/index.js",
++ "line": 1046
++ },
++ {
++ "path": "/ws/call",
++ "file": "server/index.js",
++ "line": 1048
++ },
++ {
++ "path": "/ws/game",
++ "file": "server/index.js",
++ "line": 1050
++ },
++ {
++ "path": "/ws/canvas",
++ "file": "server/index.js",
++ "line": 1050
++ },
++ {
++ "path": "/ws/robotstreamer-publish",
++ "file": "server/index.js",
++ "line": 1052
++ },
++ {
++ "path": "/ws/robotstreamer-publish",
++ "file": "server/integrations/robotstreamer-service.js",
++ "line": 807
++ },
++ {
++ "path": "/ws/broadcast",
++ "file": "server/streaming/broadcast-server.js",
++ "line": 187
++ },
++ {
++ "path": "/ws/call",
++ "file": "server/streaming/call-server.js",
++ "line": 98
++ },
++ {
++ "path": "/ws/vibe-coding/publish",
++ "file": "server/vibe-coding/publish-server.js",
++ "line": 31
++ }
++ ]
++ },
++ "env": {
++ "YAMNET_MODEL": [
++ "server/ai/audio-events.js"
++ ],
++ "YAMNET_CLASSMAP": [
++ "server/ai/audio-events.js"
++ ],
++ "YAMNET_MIN_CONFIDENCE": [
++ "server/ai/audio-events.js"
++ ],
++ "AI_VOD_MAX_WINDOWS": [
++ "server/ai/media-analysis.js"
++ ],
++ "AI_HEAR_SEGMENT_SEC": [
++ "server/ai/stream-audio.js"
++ ],
++ "AI_HEAR_MAX_SPOOL": [
++ "server/ai/stream-audio.js"
++ ],
++ "AI_MOMENTS_PATH": [
++ "server/ai/stream-memory-job.js",
++ "server/arena/arena-service.js",
++ "server/index.js"
++ ],
++ "WHISPER_BIN": [
++ "server/ai/transcribe.js"
++ ],
++ "WHISPER_MODEL": [
++ "server/ai/transcribe.js"
++ ],
++ "WHISPER_MODEL_LIVE": [
++ "server/ai/transcribe.js"
++ ],
++ "WHISPER_MODEL_MULTI": [
++ "server/ai/transcribe.js"
++ ],
++ "WHISPER_THREADS": [
++ "server/ai/transcribe.js"
++ ],
++ "WHISPER_VAD_MODEL": [
++ "server/ai/transcribe.js"
++ ],
++ "WHISPER_VAD": [
++ "server/ai/transcribe.js"
++ ],
++ "WHISPER_BEAM": [
++ "server/ai/transcribe.js"
++ ],
++ "WHISPER_MAX_CONCURRENT": [
++ "server/ai/transcribe.js"
++ ],
++ "ARENA_IMAGE_PATH": [
++ "server/arena/arena-service.js",
++ "server/index.js"
++ ],
++ "JWT_SECRET": [
++ "server/arena/arena-service.js",
++ "server/config.js",
++ "server/integrations/platform-oauth.js",
++ "server/integrations/powerchat-oauth.js"
++ ],
++ "TTS_CACHE_PATH": [
++ "server/arena/voice.js"
++ ],
++ "OV_NETWORK_URL": [
++ "server/auth/auth.js",
++ "server/config.js",
++ "server/index.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_NETWORK_PUBLIC_KEY": [
++ "server/auth/auth.js"
++ ],
++ "OV_OAUTH_CLIENT_ID": [
++ "server/auth/routes.js"
++ ],
++ "OV_OAUTH_CLIENT_SECRET": [
++ "server/auth/routes.js"
++ ],
++ "BASE_URL": [
++ "server/auth/routes.js",
++ "server/config.js"
++ ],
++ "TURN_AUTH_SECRET": [
++ "server/auth/routes.js",
++ "server/net/turn.js"
++ ],
++ "DEBUG_DM_DELIVERY": [
++ "server/chat/chat-server.js"
++ ],
++ "OV_NETWORK_INTERNAL_URL": [
++ "server/chat/chat-server.js",
++ "server/monetization/wallet-client.js",
++ "server/utils/notify.js"
++ ],
++ "INTERNAL_API_KEY": [
++ "server/chat/chat-server.js",
++ "server/config.js",
++ "server/monetization/wallet-client.js",
++ "server/streaming/routes.js",
++ "server/utils/notify.js"
++ ],
++ "OV_INTERNAL_KEY": [
++ "server/chat/chat-server.js",
++ "server/config.js",
++ "server/monetization/wallet-client.js",
++ "server/streaming/routes.js",
++ "server/utils/notify.js"
++ ],
++ "SITE_URL": [
++ "server/chat/chat-server.js"
++ ],
++ "TURN_URL": [
++ "server/config.js"
++ ],
++ "ALLOW_P2P_FALLBACK": [
++ "server/config.js"
++ ],
++ "MEDIASOUP_ANNOUNCED_IP": [
++ "server/config.js"
++ ],
++ "WHIP_PUBLIC_URL_ENABLED": [
++ "server/config.js"
++ ],
++ "WHIP_PUBLIC_URL": [
++ "server/config.js"
++ ],
++ "NODE_ENV": [
++ "server/config.js",
++ "server/index.js"
++ ],
++ "PORT": [
++ "server/config.js"
++ ],
++ "HOST": [
++ "server/config.js"
++ ],
++ "RTMP_PORT": [
++ "server/config.js"
++ ],
++ "RTMP_CHUNK_SIZE": [
++ "server/config.js"
++ ],
++ "RTMP_HOST": [
++ "server/config.js"
++ ],
++ "MEDIA_URL": [
++ "server/config.js",
++ "server/media-client.js"
++ ],
++ "MEDIA_PUBLIC_URL": [
++ "server/config.js",
++ "server/media-client.js"
++ ],
++ "MEDIA_APP_ID": [
++ "server/config.js",
++ "server/media-client.js"
++ ],
++ "TURN_USERNAME": [
++ "server/config.js"
++ ],
++ "TURN_CREDENTIAL": [
++ "server/config.js"
++ ],
++ "MEDIASOUP_LISTEN_IP": [
++ "server/config.js"
++ ],
++ "MEDIASOUP_MIN_PORT": [
++ "server/config.js"
++ ],
++ "MEDIASOUP_MAX_PORT": [
++ "server/config.js"
++ ],
++ "WEBRTC_PORT": [
++ "server/config.js"
++ ],
++ "ADMIN_USERNAME": [
++ "server/config.js"
++ ],
++ "ADMIN_PASSWORD": [
++ "server/config.js"
++ ],
++ "STAR_STREAMER": [
++ "server/config.js"
++ ],
++ "DB_PATH": [
++ "server/config.js",
++ "server/db/database.js",
++ "server/db/init.js"
++ ],
++ "JSMPEG_VIDEO_PORT": [
++ "server/config.js"
++ ],
++ "JSMPEG_AUDIO_PORT": [
++ "server/config.js"
++ ],
++ "RS_PASSTHROUGH": [
++ "server/config.js"
++ ],
++ "MIN_CASHOUT_BUCKS": [
++ "server/config.js"
++ ],
++ "ESCROW_HOLD_DAYS": [
++ "server/config.js"
++ ],
++ "VOD_PATH": [
++ "server/config.js"
++ ],
++ "COLD_STORAGE_PATH": [
++ "server/config.js"
++ ],
++ "CLIPS_PATH": [
++ "server/config.js"
++ ],
++ "MAX_VOD_SIZE_MB": [
++ "server/config.js",
++ "server/media-proxy/vods.js"
++ ],
++ "THUMBNAILS_PATH": [
++ "server/config.js"
++ ],
++ "EMOTES_PATH": [
++ "server/config.js",
++ "server/media-proxy/asset-sync.js"
++ ],
++ "MAX_EMOTE_SIZE_KB": [
++ "server/config.js"
++ ],
++ "MAX_EMOTES_PER_USER": [
++ "server/config.js"
++ ],
++ "MAX_EMOTES_PER_CHANNEL": [
++ "server/config.js"
++ ],
++ "MAX_EMOTES_PER_UPLOADER_PER_CHANNEL": [
++ "server/config.js"
++ ],
++ "FFZ_CACHE_TTL": [
++ "server/config.js"
++ ],
++ "BTTV_CACHE_TTL": [
++ "server/config.js"
++ ],
++ "SEVENTV_CACHE_TTL": [
++ "server/config.js"
++ ],
++ "SOUNDS_PATH": [
++ "server/config.js",
++ "server/media-proxy/asset-sync.js"
++ ],
++ "MAX_SOUND_SIZE_KB": [
++ "server/config.js"
++ ],
++ "SOUND_DEFAULT_MAX_SECONDS": [
++ "server/config.js"
++ ],
++ "MAX_SOUNDS_PER_CHANNEL": [
++ "server/config.js"
++ ],
++ "MAX_SOUNDS_PER_UPLOADER_PER_CHANNEL": [
++ "server/config.js"
++ ],
++ "PAYPAL_CLIENT_ID": [
++ "server/config.js"
++ ],
++ "PAYPAL_CLIENT_SECRET": [
++ "server/config.js"
++ ],
++ "PAYPAL_MODE": [
++ "server/config.js"
++ ],
++ "OWNER_USERNAME": [
++ "server/db/database.js"
++ ],
++ "OV_APP_ROOT": [
++ "server/docs/routes.js",
++ "server/web/assets.js"
++ ],
++ "OV_COMMUNITY_URL": [
++ "server/index.js",
++ "server/seo/seo.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "PASTES_ON_COMMUNITY": [
++ "server/index.js",
++ "server/seo/seo.js"
++ ],
++ "LISTEN_FDS": [
++ "server/index.js"
++ ],
++ "LISTEN_PID": [
++ "server/index.js"
++ ],
++ "YT_MIN_CREATE_INTERVAL_MS": [
++ "server/integrations/platform-oauth.js"
++ ],
++ "YT_QUOTA_COOLDOWN_MS": [
++ "server/integrations/platform-oauth.js"
++ ],
++ "YT_DAILY_QUOTA": [
++ "server/integrations/platform-oauth.js"
++ ],
++ "YT_QUOTA_SOFT": [
++ "server/integrations/platform-oauth.js"
++ ],
++ "YT_QUOTA_HARD": [
++ "server/integrations/platform-oauth.js"
++ ],
++ "RS_PASSTHROUGH_INJECT_PARAMS": [
++ "server/integrations/rs-passthrough-relay.js"
++ ],
++ "YTDLP_PATH": [
++ "server/media/media-downloader.js"
++ ],
++ "YTDLP_NODE_PATH": [
++ "server/media/media-downloader.js"
++ ],
++ "PATH": [
++ "server/media/media-downloader.js"
++ ],
++ "MEDIA_API_KEY": [
++ "server/media-client.js"
++ ],
++ "LIVE_THUMBS_PATH": [
++ "server/media-proxy/live-thumbs.js"
++ ],
++ "MEDIA_WEBHOOK_SECRET": [
++ "server/media-proxy/webhook.js"
++ ],
++ "OPS_ALERT_WEBHOOK_URL": [
++ "server/media-proxy/webhook.js"
++ ],
++ "GITHUB_TOKEN": [
++ "server/meta/routes.js"
++ ],
++ "GH_TOKEN": [
++ "server/meta/routes.js"
++ ],
++ "OFFLINE_SCREEN_PATH": [
++ "server/monetization/routes.js",
++ "server/streaming/routes.js"
++ ],
++ "OV_PUBLIC_DIR": [
++ "server/web/assets.js"
++ ],
++ "LOGIN_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_NETWORK_LOGIN_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_TOOLS_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_LIVE_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_GAMES_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_MEDIA_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_MAPS_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_FOOD_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_IMG_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_YT_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_AUDIO_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_TEXT_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_LOGO_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_DOCS_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_NET_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_DEV_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_PASTES_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ]
++ },
++ "outbound": [
++ {
++ "file": "server/chat/chat-server.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "OV_NETWORK_INTERNAL_URL",
++ "line": 84
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "server/config.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": ":4000",
++ "line": 21
++ },
++ {
++ "via": "OV_NETWORK_URL",
++ "line": 101
++ }
++ ]
++ },
++ {
++ "service": "media",
++ "refs": [
++ {
++ "via": "MEDIA_URL",
++ "line": 175
++ },
++ {
++ "via": ":4100",
++ "line": 175
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key"
++ ],
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "file": "server/integrations/discord-webhook.js",
++ "targets": [
++ {
++ "service": "webhook",
++ "refs": [
++ {
++ "via": "webhook_url (per-app, configured in DB)",
++ "line": 16
++ },
++ {
++ "via": "webhook_url (per-app, configured in DB)",
++ "line": 17
++ },
++ {
++ "via": "webhook_url (per-app, configured in DB)",
++ "line": 20
++ },
++ {
++ "via": "webhook_url (per-app, configured in DB)",
++ "line": 21
++ },
++ {
++ "via": "webhook_url (per-app, configured in DB)",
++ "line": 61
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "none-detected"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "server/integrations/platform-oauth.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/oauth/token",
++ "line": 52
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer",
++ "hmac",
++ "oauth-client"
++ ],
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "file": "server/integrations/powerchat-oauth.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/oauth/token",
++ "line": 51
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer",
++ "hmac",
++ "oauth-client"
++ ],
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "file": "server/media-client.js",
++ "targets": [
++ {
++ "service": "media",
++ "refs": [
++ {
++ "via": ":4100",
++ "line": 14
++ },
++ {
++ "via": "MEDIA_URL",
++ "line": 21
++ },
++ {
++ "via": ":4100",
++ "line": 21
++ },
++ {
++ "via": "/api/v1/${",
++ "line": 26
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "api-key",
++ "bearer"
++ ],
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "file": "server/media-proxy/webhook.js",
++ "targets": [
++ {
++ "service": "webhook",
++ "refs": [
++ {
++ "via": "webhook_url (per-app, configured in DB)",
++ "line": 38
++ },
++ {
++ "via": "webhook_url (per-app, configured in DB)",
++ "line": 52
++ },
++ {
++ "via": "webhook_url (per-app, configured in DB)",
++ "line": 53
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "hmac"
++ ],
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "file": "server/monetization/wallet-client.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/internal/coins",
++ "line": 9
++ },
++ {
++ "via": "OV_NETWORK_INTERNAL_URL",
++ "line": 18
++ },
++ {
++ "via": ":4000",
++ "line": 18
++ },
++ {
++ "via": "/internal/coins",
++ "line": 71
++ },
++ {
++ "via": "/internal/coins",
++ "line": 84
++ },
++ {
++ "via": "/internal/coins",
++ "line": 95
++ },
++ {
++ "via": "/internal/coins",
++ "line": 143
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key",
++ "bearer"
++ ],
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "file": "server/seo/seo.js",
++ "targets": [
++ {
++ "service": "community",
++ "refs": [
++ {
++ "via": "OV_COMMUNITY_URL",
++ "line": 424
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "none-detected"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "server/streaming/golive-notify.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/internal/events",
++ "line": 60
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "server/streaming/restream-manager.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/oauth/token",
++ "line": 1387
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer",
++ "oauth-client"
++ ],
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "file": "server/utils/notify.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "OV_NETWORK_INTERNAL_URL",
++ "line": 16
++ },
++ {
++ "via": ":4000",
++ "line": 16
++ },
++ {
++ "via": "/internal/notifications",
++ "line": 60
++ },
++ {
++ "via": "/internal/notifications",
++ "line": 114
++ },
++ {
++ "via": "/internal/notifications",
++ "line": 137
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "vendor/openvibe-shared/account-switcher.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 176
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "file": "vendor/openvibe-shared/auth-client.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": ":4000",
++ "line": 23
++ },
++ {
++ "via": ":4000",
++ "line": 31
++ },
++ {
++ "via": "/oauth/token",
++ "line": 60
++ },
++ {
++ "via": "/oauth/token",
++ "line": 84
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key",
++ "oauth-client"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "vendor/openvibe-shared/footer.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 280
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "vendor/openvibe-shared/navbar.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 10
++ },
++ {
++ "via": "/api/auth/me",
++ "line": 998
++ },
++ {
++ "via": "/api/auth/me",
++ "line": 1001
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": true
++ }
++ ],
++ "jobs": [
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 300000,
++ "line": 493,
++ "file": "server/ai/ai-moments-job.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 312,
++ "file": "server/ai/auto-clip-job.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 60000,
++ "line": 160,
++ "file": "server/ai/backfill-job.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 473,
++ "file": "server/ai/chat-ai.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 300000,
++ "line": 215,
++ "file": "server/ai/easter-egg-job.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 190,
++ "file": "server/ai/slogan-job.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 30000,
++ "line": 239,
++ "file": "server/ai/stream-memory-job.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 40,
++ "file": "server/ai/streamer-overview-job.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 205,
++ "file": "server/ai/timeline-job.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 79,
++ "file": "server/ai/viewers/engine.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 33,
++ "file": "server/arena/arena-job.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 39,
++ "file": "server/arena/arena-job.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 323,
++ "file": "server/arena/listener.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 250,
++ "file": "server/chat/chat-server.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 272,
++ "file": "server/chat/chat-server.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 277,
++ "file": "server/chat/chat-server.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 39,
++ "file": "server/chat/dm-routes.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 18,
++ "file": "server/diagnostics.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 300000,
++ "line": 583,
++ "file": "server/home/routes.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 130,
++ "file": "server/home/star-job.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 21600000,
++ "line": 1322,
++ "file": "server/index.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 45000,
++ "line": 1339,
++ "file": "server/index.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 1363,
++ "file": "server/index.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 1374,
++ "file": "server/index.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 615,
++ "file": "server/integrations/chat-relay-service.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 1000,
++ "line": 296,
++ "file": "server/integrations/powerchat-platform.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 430,
++ "file": "server/integrations/powerchat-platform.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 538,
++ "file": "server/integrations/powerchat-platform.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 1023,
++ "file": "server/integrations/robotstreamer-service.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 537,
++ "file": "server/integrations/rs-passthrough-relay.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 755,
++ "file": "server/integrations/rs-passthrough-relay.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 965,
++ "file": "server/integrations/rs-passthrough-relay.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 3600000,
++ "line": 550,
++ "file": "server/media/media-downloader.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 553,
++ "file": "server/media/media-downloader.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 119,
++ "file": "server/media-proxy/asset-sync.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 95,
++ "file": "server/media-proxy/clip-notify.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 3600000,
++ "line": 380,
++ "file": "server/monetization/payments.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 30000,
++ "line": 94,
++ "file": "server/news/news-service.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 28,
++ "file": "server/news/sources/base-source.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 236,
++ "file": "server/recap/recap.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 133,
++ "file": "server/streaming/broadcast-server.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 64,
++ "file": "server/streaming/call-server.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 300000,
++ "line": 76,
++ "file": "server/streaming/call-server.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 28,
++ "file": "server/streaming/live-events.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 181,
++ "file": "server/streaming/rtmp-server.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 161,
++ "file": "server/streaming/whip-handler.js"
++ },
++ {
++ "kind": "jobs.every",
++ "name": "restream-viewer-counts",
++ "intervalMs": 60000,
++ "line": 12,
++ "file": "server/utils/jobs.js"
++ },
++ {
++ "kind": "jobs.singleFlight",
++ "name": "auto-clip-backfill",
++ "intervalMs": null,
++ "line": 13,
++ "file": "server/utils/jobs.js"
++ }
++ ],
++ "contractTodos": 15,
++ "unresolvedMounts": [],
++ "testFiles": 55,
++ "ci": false
++ },
++ {
++ "name": "OpenVibe.Network",
++ "service": "network",
++ "present": true,
++ "git": {
++ "head": "71be248264060ca6d30d083571928f9a10002688",
++ "branch": "main",
++ "originMain": "71be248264060ca6d30d083571928f9a10002688",
++ "committedAt": "2026-09-21T11:57:37-07:00"
++ },
++ "sourceFiles": 77,
++ "loc": 20399,
++ "tables": [
++ {
++ "table": "admin_rate_limits",
++ "file": "server/auth/admin-limits.js",
++ "line": 24,
++ "via": "repo"
++ },
++ {
++ "table": "chrome_cache",
++ "file": "server/chrome/service.js",
++ "line": 27,
++ "via": "repo"
++ },
++ {
++ "table": "chrome_hits",
++ "file": "server/chrome/service.js",
++ "line": 29,
++ "via": "repo"
++ },
++ {
++ "table": "users",
++ "file": "server/db/database.js",
++ "line": 113,
++ "via": "repo"
++ },
++ {
++ "table": "oauth_clients",
++ "file": "server/db/database.js",
++ "line": 134,
++ "via": "repo"
++ },
++ {
++ "table": "oauth_codes",
++ "file": "server/db/database.js",
++ "line": 144,
++ "via": "repo"
++ },
++ {
++ "table": "oauth_tokens",
++ "file": "server/db/database.js",
++ "line": 158,
++ "via": "repo"
++ },
++ {
++ "table": "user_preferences",
++ "file": "server/db/database.js",
++ "line": 172,
++ "via": "repo"
++ },
++ {
++ "table": "themes",
++ "file": "server/db/database.js",
++ "line": 183,
++ "via": "repo"
++ },
++ {
++ "table": "linked_accounts",
++ "file": "server/db/database.js",
++ "line": 204,
++ "via": "repo"
++ },
++ {
++ "table": "audit_log",
++ "file": "server/db/database.js",
++ "line": 215,
++ "via": "repo"
++ },
++ {
++ "table": "ip_log",
++ "file": "server/db/database.js",
++ "line": 225,
++ "via": "repo"
++ },
++ {
++ "table": "site_settings",
++ "file": "server/db/database.js",
++ "line": 238,
++ "via": "repo"
++ },
++ {
++ "table": "url_registry",
++ "file": "server/db/database.js",
++ "line": 245,
++ "via": "repo"
++ },
++ {
++ "table": "notifications",
++ "file": "server/db/database.js",
++ "line": 263,
++ "via": "repo"
++ },
++ {
++ "table": "notification_preferences",
++ "file": "server/db/database.js",
++ "line": 290,
++ "via": "repo"
++ },
++ {
++ "table": "email_delivery_log",
++ "file": "server/db/database.js",
++ "line": 302,
++ "via": "repo"
++ },
++ {
++ "table": "password_reset_tokens",
++ "file": "server/db/database.js",
++ "line": 321,
++ "via": "repo"
++ },
++ {
++ "table": "anon_users",
++ "file": "server/db/database.js",
++ "line": 340,
++ "via": "repo"
++ },
++ {
++ "table": "user_sessions",
++ "file": "server/db/database.js",
++ "line": 354,
++ "via": "repo"
++ },
++ {
++ "table": "user_effects",
++ "file": "server/db/database.js",
++ "line": 375,
++ "via": "repo"
++ },
++ {
++ "table": "follows",
++ "file": "server/db/database.js",
++ "line": 386,
++ "via": "repo"
++ },
++ {
++ "table": "wallets",
++ "file": "server/db/database.js",
++ "line": 400,
++ "via": "repo"
++ },
++ {
++ "table": "coin_transactions",
++ "file": "server/db/database.js",
++ "line": 407,
++ "via": "repo"
++ },
++ {
++ "table": "verification_keys",
++ "file": "server/db/database.js",
++ "line": 422,
++ "via": "repo"
++ },
++ {
++ "table": "anon_ip_log",
++ "file": "server/db/database.js",
++ "line": 443,
++ "via": "repo"
++ },
++ {
++ "table": "push_subscriptions",
++ "file": "server/db/database.js",
++ "line": 460,
++ "via": "repo"
++ },
++ {
++ "table": "email_verification_tokens",
++ "file": "server/db/database.js",
++ "line": 506,
++ "via": "repo"
++ },
++ {
++ "table": "tool_domains",
++ "file": "server/domains/routes.js",
++ "line": 44,
++ "via": "repo"
++ },
++ {
++ "table": "user_history",
++ "file": "server/history/routes.js",
++ "line": 26,
++ "via": "repo"
++ },
++ {
++ "table": "stream_live_announcements",
++ "file": "server/internal/routes.js",
++ "line": 300,
++ "via": "repo"
++ },
++ {
++ "table": "url_registry",
++ "file": "server/url-registry.js",
++ "line": 7,
++ "via": "repo"
++ },
++ {
++ "table": "analytics_events",
++ "file": "packages/openvibe-shared/analytics.js",
++ "line": 74,
++ "via": "repo"
++ },
++ {
++ "table": "analytics_hourly",
++ "file": "packages/openvibe-shared/analytics.js",
++ "line": 98,
++ "via": "repo"
++ },
++ {
++ "table": "analytics_daily",
++ "file": "packages/openvibe-shared/analytics.js",
++ "line": 116,
++ "via": "repo"
++ },
++ {
++ "table": "analytics_rate_tracking",
++ "file": "packages/openvibe-shared/analytics.js",
++ "line": 137,
++ "via": "repo"
++ }
++ ],
++ "routes": [
++ {
++ "method": "DELETE",
++ "path": "/",
++ "file": "server/notifications/routes.js",
++ "line": 111
++ },
++ {
++ "method": "GET",
++ "path": "/",
++ "file": "server/discord/routes.js",
++ "line": 17
++ },
++ {
++ "method": "GET",
++ "path": "/",
++ "file": "server/notifications/routes.js",
++ "line": 16
++ },
++ {
++ "method": "PUT",
++ "path": "/",
++ "file": "server/discord/routes.js",
++ "line": 33
++ },
++ {
++ "method": "POST",
++ "path": "/:id/dismiss",
++ "file": "server/notifications/routes.js",
++ "line": 100
++ },
++ {
++ "method": "POST",
++ "path": "/:id/read",
++ "file": "server/notifications/routes.js",
++ "line": 67
++ },
++ {
++ "method": "GET",
++ "path": "/.well-known/web-identity",
++ "file": "server/index.js",
++ "line": 572
++ },
++ {
++ "method": "POST",
++ "path": "/admin",
++ "file": "server/setup/routes.js",
++ "line": 114
++ },
++ {
++ "method": "GET",
++ "path": "/api/.well-known/jwks",
++ "file": "server/index.js",
++ "line": 356
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/domains",
++ "file": "server/domains/routes.js",
++ "line": 176
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/domains",
++ "file": "server/domains/routes.js",
++ "line": 197
++ },
++ {
++ "method": "POST",
++ "path": "/api/admin/domains",
++ "file": "server/domains/routes.js",
++ "line": 224
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/admin/domains/:id",
++ "file": "server/domains/routes.js",
++ "line": 256
++ },
++ {
++ "method": "PUT",
++ "path": "/api/admin/domains/:id",
++ "file": "server/domains/routes.js",
++ "line": 238
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/domains/catalog",
++ "file": "server/domains/routes.js",
++ "line": 206
++ },
++ {
++ "method": "POST",
++ "path": "/api/admin/domains/check",
++ "file": "server/domains/routes.js",
++ "line": 217
++ },
++ {
++ "method": "GET",
++ "path": "/api/admin/ssh-info",
++ "file": "server/index.js",
++ "line": 452
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/anon-identities",
++ "file": "server/auth/routes.js",
++ "line": 635
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/anon-session",
++ "file": "server/auth/routes.js",
++ "line": 512
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/anon/:token",
++ "file": "server/auth/routes.js",
++ "line": 582
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/anon/:token/link",
++ "file": "server/auth/routes.js",
++ "line": 619
++ },
++ {
++ "method": "PUT",
++ "path": "/api/auth/anon/:token/preferences",
++ "file": "server/auth/routes.js",
++ "line": 596
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/change-password",
++ "file": "server/auth/routes.js",
++ "line": 422
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/discord/callback",
++ "file": "server/auth/discord-link.js",
++ "line": 61
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/auth/discord/link",
++ "file": "server/auth/discord-link.js",
++ "line": 158
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/discord/link",
++ "file": "server/auth/discord-link.js",
++ "line": 31
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/discord/status",
++ "file": "server/auth/discord-link.js",
++ "line": 178
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/email/send-verification",
++ "file": "server/auth/email-verify.js",
++ "line": 99
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/email/status",
++ "file": "server/auth/email-verify.js",
++ "line": 83
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/email/verify",
++ "file": "server/auth/email-verify.js",
++ "line": 122
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/forgot-password",
++ "file": "server/auth/routes.js",
++ "line": 206
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/login",
++ "file": "server/auth/routes.js",
++ "line": 183
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/logout",
++ "file": "server/auth/routes.js",
++ "line": 506
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/me",
++ "file": "server/auth/routes.js",
++ "line": 362
++ },
++ {
++ "method": "PUT",
++ "path": "/api/auth/profile",
++ "file": "server/auth/routes.js",
++ "line": 372
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/refresh",
++ "file": "server/auth/routes.js",
++ "line": 300
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/register",
++ "file": "server/auth/routes.js",
++ "line": 88
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/reset-password",
++ "file": "server/auth/routes.js",
++ "line": 254
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/reset-password/validate",
++ "file": "server/auth/routes.js",
++ "line": 238
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/auth/sessions",
++ "file": "server/auth/routes.js",
++ "line": 717
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/sessions",
++ "file": "server/auth/routes.js",
++ "line": 673
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/sessions",
++ "file": "server/auth/routes.js",
++ "line": 686
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/auth/sessions/:id",
++ "file": "server/auth/routes.js",
++ "line": 708
++ },
++ {
++ "method": "GET",
++ "path": "/api/auth/users/:id/card",
++ "file": "server/auth/routes.js",
++ "line": 728
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/auth/users/:id/follow",
++ "file": "server/auth/routes.js",
++ "line": 811
++ },
++ {
++ "method": "POST",
++ "path": "/api/auth/users/:id/follow",
++ "file": "server/auth/routes.js",
++ "line": 780
++ },
++ {
++ "method": "GET",
++ "path": "/api/brand",
++ "file": "server/index.js",
++ "line": 373
++ },
++ {
++ "method": "GET",
++ "path": "/api/catalog.json",
++ "file": "server/index.js",
++ "line": 423
++ },
++ {
++ "method": "GET",
++ "path": "/api/chrome",
++ "file": "server/chrome/service.js",
++ "line": 137
++ },
++ {
++ "method": "POST",
++ "path": "/api/chrome/hit",
++ "file": "server/chrome/service.js",
++ "line": 150
++ },
++ {
++ "method": "GET",
++ "path": "/api/domains",
++ "file": "server/domains/routes.js",
++ "line": 176
++ },
++ {
++ "method": "GET",
++ "path": "/api/domains",
++ "file": "server/domains/routes.js",
++ "line": 197
++ },
++ {
++ "method": "POST",
++ "path": "/api/domains",
++ "file": "server/domains/routes.js",
++ "line": 224
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/domains/:id",
++ "file": "server/domains/routes.js",
++ "line": 256
++ },
++ {
++ "method": "PUT",
++ "path": "/api/domains/:id",
++ "file": "server/domains/routes.js",
++ "line": 238
++ },
++ {
++ "method": "GET",
++ "path": "/api/domains/catalog",
++ "file": "server/domains/routes.js",
++ "line": 206
++ },
++ {
++ "method": "POST",
++ "path": "/api/domains/check",
++ "file": "server/domains/routes.js",
++ "line": 217
++ },
++ {
++ "method": "GET",
++ "path": "/api/health",
++ "file": "server/index.js",
++ "line": 368
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/history",
++ "file": "server/history/routes.js",
++ "line": 143
++ },
++ {
++ "method": "GET",
++ "path": "/api/history",
++ "file": "server/history/routes.js",
++ "line": 92
++ },
++ {
++ "method": "POST",
++ "path": "/api/history",
++ "file": "server/history/routes.js",
++ "line": 108
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/history/:id",
++ "file": "server/history/routes.js",
++ "line": 148
++ },
++ {
++ "method": "PUT",
++ "path": "/api/history/settings",
++ "file": "server/history/routes.js",
++ "line": 137
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/profile/avatar",
++ "file": "server/profile/avatar.js",
++ "line": 107
++ },
++ {
++ "method": "PUT",
++ "path": "/api/profile/avatar",
++ "file": "server/profile/avatar.js",
++ "line": 96
++ },
++ {
++ "method": "GET",
++ "path": "/api/profile/avatar/:username",
++ "file": "server/profile/avatar.js",
++ "line": 110
++ },
++ {
++ "method": "PUT",
++ "path": "/api/push/live-preferences",
++ "file": "server/push/routes.js",
++ "line": 60
++ },
++ {
++ "method": "GET",
++ "path": "/api/push/status",
++ "file": "server/push/routes.js",
++ "line": 37
++ },
++ {
++ "method": "POST",
++ "path": "/api/push/subscribe",
++ "file": "server/push/routes.js",
++ "line": 17
++ },
++ {
++ "method": "POST",
++ "path": "/api/push/unsubscribe",
++ "file": "server/push/routes.js",
++ "line": 29
++ },
++ {
++ "method": "GET",
++ "path": "/api/push/vapid-key",
++ "file": "server/push/routes.js",
++ "line": 10
++ },
++ {
++ "method": "GET",
++ "path": "/api/sso/targets",
++ "file": "server/index.js",
++ "line": 405
++ },
++ {
++ "method": "GET",
++ "path": "/api/themes",
++ "file": "server/themes/routes.js",
++ "line": 41
++ },
++ {
++ "method": "POST",
++ "path": "/api/themes",
++ "file": "server/themes/routes.js",
++ "line": 148
++ },
++ {
++ "method": "GET",
++ "path": "/api/themes/:idOrSlug",
++ "file": "server/themes/routes.js",
++ "line": 109
++ },
++ {
++ "method": "PUT",
++ "path": "/api/themes/me",
++ "file": "server/themes/routes.js",
++ "line": 119
++ },
++ {
++ "method": "GET",
++ "path": "/api/themes/me/active",
++ "file": "server/themes/routes.js",
++ "line": 74
++ },
++ {
++ "method": "PUT",
++ "path": "/api/themes/me/display",
++ "file": "server/themes/routes.js",
++ "line": 99
++ },
++ {
++ "method": "POST",
++ "path": "/api/webhooks/resend",
++ "file": "server/notifications/resend-webhook.js",
++ "line": 26
++ },
++ {
++ "method": "GET",
++ "path": "/audit",
++ "file": "server/admin/routes.js",
++ "line": 797
++ },
++ {
++ "method": "DELETE",
++ "path": "/avatar",
++ "file": "server/profile/avatar.js",
++ "line": 107
++ },
++ {
++ "method": "PUT",
++ "path": "/avatar",
++ "file": "server/profile/avatar.js",
++ "line": 96
++ },
++ {
++ "method": "GET",
++ "path": "/avatar/:username",
++ "file": "server/profile/avatar.js",
++ "line": 110
++ },
++ {
++ "method": "POST",
++ "path": "/bootstrap",
++ "file": "server/setup/routes.js",
++ "line": 99
++ },
++ {
++ "method": "GET",
++ "path": "/bots",
++ "file": "server/admin/analytics-routes.js",
++ "line": 255
++ },
++ {
++ "method": "POST",
++ "path": "/broadcast",
++ "file": "server/admin/routes.js",
++ "line": 721
++ },
++ {
++ "method": "GET",
++ "path": "/certs",
++ "file": "server/deploy/routes.js",
++ "line": 139
++ },
++ {
++ "method": "POST",
++ "path": "/certs/issue-cloudflare",
++ "file": "server/deploy/routes.js",
++ "line": 164
++ },
++ {
++ "method": "POST",
++ "path": "/certs/issue-manual",
++ "file": "server/deploy/routes.js",
++ "line": 236
++ },
++ {
++ "method": "POST",
++ "path": "/certs/manual-info",
++ "file": "server/deploy/routes.js",
++ "line": 215
++ },
++ {
++ "method": "POST",
++ "path": "/certs/renew",
++ "file": "server/deploy/routes.js",
++ "line": 281
++ },
++ {
++ "method": "GET",
++ "path": "/config",
++ "file": "server/deploy/routes.js",
++ "line": 81
++ },
++ {
++ "method": "PUT",
++ "path": "/config",
++ "file": "server/deploy/routes.js",
++ "line": 95
++ },
++ {
++ "method": "POST",
++ "path": "/deploy",
++ "file": "server/setup/routes.js",
++ "line": 239
++ },
++ {
++ "method": "GET",
++ "path": "/deploy-status",
++ "file": "server/setup/routes.js",
++ "line": 309
++ },
++ {
++ "method": "GET",
++ "path": "/domains",
++ "file": "server/deploy/routes.js",
++ "line": 406
++ },
++ {
++ "method": "PUT",
++ "path": "/domains",
++ "file": "server/deploy/routes.js",
++ "line": 426
++ },
++ {
++ "method": "GET",
++ "path": "/email",
++ "file": "server/admin/routes.js",
++ "line": 240
++ },
++ {
++ "method": "PUT",
++ "path": "/email",
++ "file": "server/admin/routes.js",
++ "line": 250
++ },
++ {
++ "method": "POST",
++ "path": "/email/test",
++ "file": "server/admin/routes.js",
++ "line": 283
++ },
++ {
++ "method": "GET",
++ "path": "/fedcm/accounts",
++ "file": "server/auth/fedcm.js",
++ "line": 111
++ },
++ {
++ "method": "POST",
++ "path": "/fedcm/assertion",
++ "file": "server/auth/fedcm.js",
++ "line": 125
++ },
++ {
++ "method": "GET",
++ "path": "/fedcm/client-metadata",
++ "file": "server/auth/fedcm.js",
++ "line": 120
++ },
++ {
++ "method": "GET",
++ "path": "/fedcm/config.json",
++ "file": "server/auth/fedcm.js",
++ "line": 97
++ },
++ {
++ "method": "POST",
++ "path": "/fedcm/disconnect",
++ "file": "server/auth/fedcm.js",
++ "line": 142
++ },
++ {
++ "method": "GET",
++ "path": "/health",
++ "file": "server/admin/routes.js",
++ "line": 766
++ },
++ {
++ "method": "POST",
++ "path": "/identity",
++ "file": "server/setup/routes.js",
++ "line": 152
++ },
++ {
++ "method": "GET",
++ "path": "/internal/anon-admin",
++ "file": "server/internal/routes.js",
++ "line": 618
++ },
++ {
++ "method": "GET",
++ "path": "/internal/anon-list",
++ "file": "server/internal/routes.js",
++ "line": 667
++ },
++ {
++ "method": "POST",
++ "path": "/internal/audit",
++ "file": "server/internal/routes.js",
++ "line": 156
++ },
++ {
++ "method": "POST",
++ "path": "/internal/coins/credit",
++ "file": "server/internal/routes.js",
++ "line": 242
++ },
++ {
++ "method": "POST",
++ "path": "/internal/coins/debit",
++ "file": "server/internal/routes.js",
++ "line": 254
++ },
++ {
++ "method": "GET",
++ "path": "/internal/coins/stats",
++ "file": "server/internal/routes.js",
++ "line": 204
++ },
++ {
++ "method": "POST",
++ "path": "/internal/coins/transfer",
++ "file": "server/internal/routes.js",
++ "line": 267
++ },
++ {
++ "method": "POST",
++ "path": "/internal/events/stream-live",
++ "file": "server/internal/routes.js",
++ "line": 284
++ },
++ {
++ "method": "POST",
++ "path": "/internal/issue-token",
++ "file": "server/internal/routes.js",
++ "line": 508
++ },
++ {
++ "method": "POST",
++ "path": "/internal/link-account",
++ "file": "server/internal/routes.js",
++ "line": 107
++ },
++ {
++ "method": "POST",
++ "path": "/internal/notifications/mark-read",
++ "file": "server/internal/routes.js",
++ "line": 469
++ },
++ {
++ "method": "POST",
++ "path": "/internal/notifications/push",
++ "file": "server/internal/routes.js",
++ "line": 411
++ },
++ {
++ "method": "POST",
++ "path": "/internal/notifications/push-bulk",
++ "file": "server/internal/routes.js",
++ "line": 431
++ },
++ {
++ "method": "POST",
++ "path": "/internal/notifications/resolve-users",
++ "file": "server/internal/routes.js",
++ "line": 488
++ },
++ {
++ "method": "GET",
++ "path": "/internal/notifications/unread/:userId",
++ "file": "server/internal/routes.js",
++ "line": 454
++ },
++ {
++ "method": "POST",
++ "path": "/internal/resolve-anon",
++ "file": "server/internal/routes.js",
++ "line": 539
++ },
++ {
++ "method": "GET",
++ "path": "/internal/stats",
++ "file": "server/internal/routes.js",
++ "line": 165
++ },
++ {
++ "method": "GET",
++ "path": "/internal/url-registry/resolved",
++ "file": "server/internal/routes.js",
++ "line": 174
++ },
++ {
++ "method": "POST",
++ "path": "/internal/user-avatar",
++ "file": "server/internal/routes.js",
++ "line": 141
++ },
++ {
++ "method": "GET",
++ "path": "/internal/users/:id",
++ "file": "server/internal/routes.js",
++ "line": 58
++ },
++ {
++ "method": "GET",
++ "path": "/internal/users/:id/linked-accounts",
++ "file": "server/internal/routes.js",
++ "line": 149
++ },
++ {
++ "method": "GET",
++ "path": "/internal/users/:id/theme",
++ "file": "server/internal/routes.js",
++ "line": 95
++ },
++ {
++ "method": "POST",
++ "path": "/internal/users/bulk",
++ "file": "server/internal/routes.js",
++ "line": 80
++ },
++ {
++ "method": "GET",
++ "path": "/internal/users/by-username/:username",
++ "file": "server/internal/routes.js",
++ "line": 69
++ },
++ {
++ "method": "POST",
++ "path": "/internal/verify-token",
++ "file": "server/internal/routes.js",
++ "line": 36
++ },
++ {
++ "method": "GET",
++ "path": "/llms.txt",
++ "file": "server/index.js",
++ "line": 550
++ },
++ {
++ "method": "GET",
++ "path": "/me",
++ "file": "server/coins/routes.js",
++ "line": 18
++ },
++ {
++ "method": "GET",
++ "path": "/me/history",
++ "file": "server/coins/routes.js",
++ "line": 24
++ },
++ {
++ "method": "GET",
++ "path": "/net-config",
++ "file": "server/admin/routes.js",
++ "line": 907
++ },
++ {
++ "method": "PUT",
++ "path": "/net-config",
++ "file": "server/admin/routes.js",
++ "line": 926
++ },
++ {
++ "method": "GET",
++ "path": "/newest",
++ "file": "server/notifications/routes.js",
++ "line": 57
++ },
++ {
++ "method": "POST",
++ "path": "/nginx/apply",
++ "file": "server/deploy/routes.js",
++ "line": 343
++ },
++ {
++ "method": "GET",
++ "path": "/nginx/preview",
++ "file": "server/deploy/routes.js",
++ "line": 296
++ },
++ {
++ "method": "GET",
++ "path": "/nginx/preview/:serviceId",
++ "file": "server/deploy/routes.js",
++ "line": 314
++ },
++ {
++ "method": "POST",
++ "path": "/nginx/reload",
++ "file": "server/deploy/routes.js",
++ "line": 382
++ },
++ {
++ "method": "POST",
++ "path": "/nginx/validate",
++ "file": "server/deploy/routes.js",
++ "line": 330
++ },
++ {
++ "method": "GET",
++ "path": "/oauth/.well-known/openid-configuration",
++ "file": "server/auth/oauth-routes.js",
++ "line": 304
++ },
++ {
++ "method": "GET",
++ "path": "/oauth/authorize",
++ "file": "server/auth/oauth-routes.js",
++ "line": 20
++ },
++ {
++ "method": "POST",
++ "path": "/oauth/confirm",
++ "file": "server/auth/oauth-routes.js",
++ "line": 78
++ },
++ {
++ "method": "POST",
++ "path": "/oauth/token",
++ "file": "server/auth/oauth-routes.js",
++ "line": 131
++ },
++ {
++ "method": "GET",
++ "path": "/openvibe-sw.js",
++ "file": "server/index.js",
++ "line": 596
++ },
++ {
++ "method": "GET",
++ "path": "/overview",
++ "file": "server/admin/analytics-routes.js",
++ "line": 75
++ },
++ {
++ "method": "GET",
++ "path": "/preferences",
++ "file": "server/notifications/routes.js",
++ "line": 121
++ },
++ {
++ "method": "PUT",
++ "path": "/preferences",
++ "file": "server/notifications/routes.js",
++ "line": 132
++ },
++ {
++ "method": "DELETE",
++ "path": "/preferences/:category",
++ "file": "server/notifications/routes.js",
++ "line": 149
++ },
++ {
++ "method": "GET",
++ "path": "/prerequisites",
++ "file": "server/deploy/routes.js",
++ "line": 57
++ },
++ {
++ "method": "POST",
++ "path": "/read-all",
++ "file": "server/notifications/routes.js",
++ "line": 89
++ },
++ {
++ "method": "POST",
++ "path": "/read-batch",
++ "file": "server/notifications/routes.js",
++ "line": 78
++ },
++ {
++ "method": "GET",
++ "path": "/realtime",
++ "file": "server/admin/analytics-routes.js",
++ "line": 285
++ },
++ {
++ "method": "POST",
++ "path": "/reinit",
++ "file": "server/discord/routes.js",
++ "line": 96
++ },
++ {
++ "method": "POST",
++ "path": "/reset-db",
++ "file": "server/admin/routes.js",
++ "line": 473
++ },
++ {
++ "method": "GET",
++ "path": "/service/:name",
++ "file": "server/admin/analytics-routes.js",
++ "line": 224
++ },
++ {
++ "method": "GET",
++ "path": "/settings",
++ "file": "server/admin/routes.js",
++ "line": 300
++ },
++ {
++ "method": "PUT",
++ "path": "/settings",
++ "file": "server/admin/routes.js",
++ "line": 328
++ },
++ {
++ "method": "GET",
++ "path": "/sso/check",
++ "file": "server/index.js",
++ "line": 577
++ },
++ {
++ "method": "GET",
++ "path": "/status",
++ "file": "server/setup/routes.js",
++ "line": 91
++ },
++ {
++ "method": "POST",
++ "path": "/test",
++ "file": "server/discord/routes.js",
++ "line": 77
++ },
++ {
++ "method": "POST",
++ "path": "/test-live",
++ "file": "server/discord/routes.js",
++ "line": 87
++ },
++ {
++ "method": "GET",
++ "path": "/tos",
++ "file": "server/index.js",
++ "line": 419
++ },
++ {
++ "method": "GET",
++ "path": "/unread-by-category",
++ "file": "server/notifications/routes.js",
++ "line": 47
++ },
++ {
++ "method": "GET",
++ "path": "/unread-count",
++ "file": "server/notifications/routes.js",
++ "line": 37
++ },
++ {
++ "method": "GET",
++ "path": "/url-registry",
++ "file": "server/admin/routes.js",
++ "line": 353
++ },
++ {
++ "method": "PUT",
++ "path": "/url-registry",
++ "file": "server/admin/routes.js",
++ "line": 380
++ },
++ {
++ "method": "DELETE",
++ "path": "/url-registry/:key",
++ "file": "server/admin/routes.js",
++ "line": 423
++ },
++ {
++ "method": "PUT",
++ "path": "/url-registry/:key",
++ "file": "server/admin/routes.js",
++ "line": 401
++ },
++ {
++ "method": "POST",
++ "path": "/url-registry/:key/reset",
++ "file": "server/admin/routes.js",
++ "line": 458
++ },
++ {
++ "method": "POST",
++ "path": "/url-registry/refresh-all",
++ "file": "server/admin/routes.js",
++ "line": 438
++ },
++ {
++ "method": "POST",
++ "path": "/urls",
++ "file": "server/setup/routes.js",
++ "line": 193
++ },
++ {
++ "method": "GET",
++ "path": "/users",
++ "file": "server/admin/routes.js",
++ "line": 536
++ },
++ {
++ "method": "PUT",
++ "path": "/users/:id/ban",
++ "file": "server/admin/routes.js",
++ "line": 691
++ },
++ {
++ "method": "PUT",
++ "path": "/users/:id/email",
++ "file": "server/admin/routes.js",
++ "line": 568
++ },
++ {
++ "method": "PUT",
++ "path": "/users/:id/role",
++ "file": "server/admin/routes.js",
++ "line": 666
++ },
++ {
++ "method": "POST",
++ "path": "/users/:id/send-reset",
++ "file": "server/admin/routes.js",
++ "line": 607
++ },
++ {
++ "method": "POST",
++ "path": "/users/grant-admin",
++ "file": "server/admin/routes.js",
++ "line": 500
++ },
++ {
++ "method": "GET",
++ "path": "/verification-keys",
++ "file": "server/admin/routes.js",
++ "line": 824
++ },
++ {
++ "method": "POST",
++ "path": "/verification-keys",
++ "file": "server/admin/routes.js",
++ "line": 833
++ },
++ {
++ "method": "DELETE",
++ "path": "/verification-keys/:id",
++ "file": "server/admin/routes.js",
++ "line": 881
++ },
++ {
++ "method": "GET",
++ "path": "/verify-email",
++ "file": "server/index.js",
++ "line": 562
++ }
++ ],
++ "ws": {
++ "servers": [],
++ "paths": [
++ {
++ "path": "/ws/",
++ "file": "server/deploy/nginx-generator.js",
++ "line": 150
++ }
++ ]
++ },
++ "env": {
++ "BASE_URL": [
++ "server/admin/routes.js",
++ "server/config.js",
++ "server/index.js"
++ ],
++ "OV_LIVE_INTERNAL_URL": [
++ "server/admin/routes.js",
++ "server/config.js"
++ ],
++ "INTERNAL_API_KEY": [
++ "server/admin/routes.js",
++ "server/config.js"
++ ],
++ "OV_INTERNAL_KEY": [
++ "server/admin/routes.js"
++ ],
++ "OWNER_USERNAME": [
++ "server/admin/routes.js",
++ "server/auth/owner-guard.js",
++ "server/auth/routes.js",
++ "server/deploy/routes.js",
++ "server/index.js"
++ ],
++ "NODE_ENV": [
++ "server/auth/fedcm.js",
++ "server/auth/sso-check.js",
++ "server/config.js",
++ "server/db/database.js",
++ "server/index.js"
++ ],
++ "OV_NETWORK_BASE_URL": [
++ "server/auth/reset-tokens.js"
++ ],
++ "OV_SSO_TARGETS": [
++ "server/auth/sso-targets.js"
++ ],
++ "OV_SSO_TARGETS_DISABLED": [
++ "server/auth/sso-targets.js"
++ ],
++ "PORT": [
++ "server/config.js"
++ ],
++ "HOST": [
++ "server/config.js"
++ ],
++ "OV_NETWORK_URL": [
++ "server/config.js",
++ "packages/openvibe-shared/brand.js"
++ ],
++ "LOGIN_URL": [
++ "server/config.js",
++ "packages/openvibe-shared/brand.js"
++ ],
++ "INTERNAL_URL": [
++ "server/config.js"
++ ],
++ "OV_NETWORK_INTERNAL_URL": [
++ "server/config.js"
++ ],
++ "SETUP_TOKEN": [
++ "server/config.js"
++ ],
++ "BOOTSTRAP_PROFILE": [
++ "server/config.js",
++ "server/db/database.js"
++ ],
++ "JWT_PRIVATE_KEY": [
++ "server/config.js"
++ ],
++ "JWT_PUBLIC_KEY": [
++ "server/config.js"
++ ],
++ "DB_PATH": [
++ "server/config.js",
++ "server/grant-admin.js",
++ "server/reset-db.js"
++ ],
++ "ADMIN_USERNAME": [
++ "server/config.js"
++ ],
++ "ADMIN_PASSWORD": [
++ "server/config.js"
++ ],
++ "AVATAR_PATH": [
++ "server/config.js"
++ ],
++ "OV_LIVE_WEBHOOK_SECRET": [
++ "server/config.js"
++ ],
++ "OV_TOOLS_INTERNAL_URL": [
++ "server/config.js",
++ "server/domains/catalog.js"
++ ],
++ "OV_GAMES_INTERNAL_URL": [
++ "server/config.js"
++ ],
++ "OV_GAMES_WEBHOOK_SECRET": [
++ "server/config.js"
++ ],
++ "OV_MEDIA_INTERNAL_URL": [
++ "server/config.js"
++ ],
++ "OPENVIBELIVE_DB_PATH": [
++ "server/db/database.js"
++ ],
++ "CERTBOT_BIN": [
++ "server/deploy/cert-manager.js"
++ ],
++ "LETSENCRYPT_DIR": [
++ "server/deploy/cert-manager.js"
++ ],
++ "PUBLIC_IP": [
++ "server/domains/dns-check.js"
++ ],
++ "MEDIASOUP_ANNOUNCED_IP": [
++ "server/domains/dns-check.js"
++ ],
++ "SSH_SERVER_HOST": [
++ "server/index.js"
++ ],
++ "SSH_PROJECTS_ROOT": [
++ "server/index.js"
++ ],
++ "OV_NETWORK_LOGIN_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_TOOLS_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_LIVE_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_GAMES_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_MEDIA_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_COMMUNITY_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_MAPS_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_FOOD_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_IMG_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_YT_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_AUDIO_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_TEXT_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_LOGO_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_DOCS_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_NET_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_DEV_URL": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "OV_PASTES_URL": [
++ "packages/openvibe-shared/brand.js"
++ ]
++ },
++ "outbound": [
++ {
++ "file": "server/admin/analytics-routes.js",
++ "targets": [
++ {
++ "service": "live",
++ "refs": [
++ {
++ "via": ":3000",
++ "line": 22
++ }
++ ]
++ },
++ {
++ "service": "tools",
++ "refs": [
++ {
++ "via": ":4001",
++ "line": 23
++ }
++ ]
++ },
++ {
++ "service": "games",
++ "refs": [
++ {
++ "via": ":8000",
++ "line": 24
++ }
++ ]
++ },
++ {
++ "service": "media",
++ "refs": [
++ {
++ "via": ":4100",
++ "line": 25
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key",
++ "bearer"
++ ],
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "file": "server/admin/routes.js",
++ "targets": [
++ {
++ "service": "live",
++ "refs": [
++ {
++ "via": "live.internalUrl",
++ "line": 69
++ },
++ {
++ "via": "OV_LIVE_INTERNAL_URL",
++ "line": 219
++ },
++ {
++ "via": ":3000",
++ "line": 219
++ }
++ ]
++ },
++ {
++ "service": "tools",
++ "refs": [
++ {
++ "via": "tools.internalUrl",
++ "line": 70
++ }
++ ]
++ },
++ {
++ "service": "games",
++ "refs": [
++ {
++ "via": "games.internalUrl",
++ "line": 71
++ }
++ ]
++ },
++ {
++ "service": "media",
++ "refs": [
++ {
++ "via": "media.internalUrl",
++ "line": 72
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key"
++ ],
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "file": "server/chrome/service.js",
++ "targets": [
++ {
++ "service": "live",
++ "refs": [
++ {
++ "via": ":3000",
++ "line": 46
++ },
++ {
++ "via": ":3000",
++ "line": 105
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key"
++ ],
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "file": "server/domains/catalog.js",
++ "targets": [
++ {
++ "service": "tools",
++ "refs": [
++ {
++ "via": "OV_TOOLS_INTERNAL_URL",
++ "line": 17
++ },
++ {
++ "via": ":4001",
++ "line": 17
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "none-detected"
++ ],
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "file": "server/index.js",
++ "targets": [
++ {
++ "service": "live",
++ "refs": [
++ {
++ "via": ":3000",
++ "line": 215
++ },
++ {
++ "via": "live.internalUrl",
++ "line": 289
++ },
++ {
++ "via": "live.internalUrl",
++ "line": 477
++ }
++ ]
++ },
++ {
++ "service": "tools",
++ "refs": [
++ {
++ "via": ":4001",
++ "line": 217
++ },
++ {
++ "via": "tools.internalUrl",
++ "line": 290
++ }
++ ]
++ },
++ {
++ "service": "media",
++ "refs": [
++ {
++ "via": ":4100",
++ "line": 218
++ },
++ {
++ "via": "media.internalUrl",
++ "line": 292
++ }
++ ]
++ },
++ {
++ "service": "games",
++ "refs": [
++ {
++ "via": ":8000",
++ "line": 219
++ },
++ {
++ "via": "games.internalUrl",
++ "line": 291
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key",
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "server/profile/avatar.js",
++ "targets": [
++ {
++ "service": "live",
++ "refs": [
++ {
++ "via": "live.internalUrl",
++ "line": 64
++ },
++ {
++ "via": ":3000",
++ "line": 64
++ }
++ ]
++ },
++ {
++ "service": "media",
++ "refs": [
++ {
++ "via": "media.internalUrl",
++ "line": 72
++ },
++ {
++ "via": ":4100",
++ "line": 72
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key"
++ ],
++ "timeout": true,
++ "retry": false
++ }
++ ],
++ "jobs": [
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 20,
++ "file": "server/auth/discord-link.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 167,
++ "file": "server/chrome/service.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 169,
++ "file": "server/chrome/service.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 3600000,
++ "line": 652,
++ "file": "server/index.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 120000,
++ "line": 655,
++ "file": "server/index.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 658,
++ "file": "server/index.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 900000,
++ "line": 624,
++ "file": "packages/openvibe-shared/account-switcher.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 5000,
++ "line": 238,
++ "file": "packages/openvibe-shared/analytics.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 3600000,
++ "line": 241,
++ "file": "packages/openvibe-shared/analytics.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 247,
++ "file": "packages/openvibe-shared/analytics.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 548,
++ "file": "packages/openvibe-shared/notification-ui.js"
++ }
++ ],
++ "contractTodos": 0,
++ "unresolvedMounts": [],
++ "testFiles": 17,
++ "ci": false
++ },
++ {
++ "name": "OpenVibe.Media",
++ "service": "media",
++ "present": true,
++ "git": {
++ "head": "6114bc61c466cd51335548d0ce87058df125c577",
++ "branch": "main",
++ "originMain": "6114bc61c466cd51335548d0ce87058df125c577",
++ "committedAt": "2026-09-21T09:58:34-07:00"
++ },
++ "sourceFiles": 28,
++ "loc": 8702,
++ "tables": [
++ {
++ "table": "apps",
++ "file": "server/db/schema.sql",
++ "line": 8,
++ "via": "repo"
++ },
++ {
++ "table": "vods",
++ "file": "server/db/schema.sql",
++ "line": 22,
++ "via": "repo"
++ },
++ {
++ "table": "clips",
++ "file": "server/db/schema.sql",
++ "line": 63,
++ "via": "repo"
++ },
++ {
++ "table": "content_views",
++ "file": "server/db/schema.sql",
++ "line": 94,
++ "via": "repo"
++ },
++ {
++ "table": "pastes",
++ "file": "server/db/schema.sql",
++ "line": 105,
++ "via": "repo"
++ },
++ {
++ "table": "paste_likes",
++ "file": "server/db/schema.sql",
++ "line": 138,
++ "via": "repo"
++ },
++ {
++ "table": "paste_comments",
++ "file": "server/db/schema.sql",
++ "line": 146,
++ "via": "repo"
++ },
++ {
++ "table": "files",
++ "file": "server/db/schema.sql",
++ "line": 164,
++ "via": "repo"
++ },
++ {
++ "table": "media_settings",
++ "file": "server/db/schema.sql",
++ "line": 178,
++ "via": "repo"
++ },
++ {
++ "table": "assets",
++ "file": "server/db/schema.sql",
++ "line": 189,
++ "via": "repo"
++ },
++ {
++ "table": "content_visits",
++ "file": "server/views/service.js",
++ "line": 43,
++ "via": "repo"
++ },
++ {
++ "table": "analytics_events",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 74,
++ "via": "vendored openvibe-shared"
++ },
++ {
++ "table": "analytics_hourly",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 98,
++ "via": "vendored openvibe-shared"
++ },
++ {
++ "table": "analytics_daily",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 116,
++ "via": "vendored openvibe-shared"
++ },
++ {
++ "table": "analytics_rate_tracking",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 137,
++ "via": "vendored openvibe-shared"
++ }
++ ],
++ "routes": [
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/admin/storage",
++ "file": "server/admin/routes.js",
++ "line": 72
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/admin/storage/buckets",
++ "file": "server/admin/routes.js",
++ "line": 403
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/admin/storage/tiers",
++ "file": "server/admin/routes.js",
++ "line": 257
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/admin/storage/tiers/bulk-move",
++ "file": "server/admin/routes.js",
++ "line": 376
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/admin/storage/tiers/move",
++ "file": "server/admin/routes.js",
++ "line": 358
++ },
++ {
++ "method": "PUT",
++ "path": "/api/v1/:app/admin/storage/tiers/settings",
++ "file": "server/admin/routes.js",
++ "line": 305
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/admin/storage/tiers/sweep",
++ "file": "server/admin/routes.js",
++ "line": 331
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/admin/storage/vods",
++ "file": "server/admin/routes.js",
++ "line": 127
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/v1/:app/admin/storage/vods/bulk",
++ "file": "server/admin/routes.js",
++ "line": 199
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/assets",
++ "file": "server/assets/routes.js",
++ "line": 72
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/assets",
++ "file": "server/assets/routes.js",
++ "line": 39
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/v1/:app/assets/:id",
++ "file": "server/assets/routes.js",
++ "line": 84
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/clips",
++ "file": "server/vod/clips-routes.js",
++ "line": 283
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/clips",
++ "file": "server/vod/clips-routes.js",
++ "line": 114
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/v1/:app/clips/:id",
++ "file": "server/vod/clips-routes.js",
++ "line": 363
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/clips/:id",
++ "file": "server/vod/clips-routes.js",
++ "line": 309
++ },
++ {
++ "method": "PUT",
++ "path": "/api/v1/:app/clips/:id",
++ "file": "server/vod/clips-routes.js",
++ "line": 345
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/clips/:id/recut",
++ "file": "server/vod/clips-routes.js",
++ "line": 328
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/files",
++ "file": "server/files/routes.js",
++ "line": 139
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/files",
++ "file": "server/files/routes.js",
++ "line": 75
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/v1/:app/files/:key",
++ "file": "server/files/routes.js",
++ "line": 167
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/files/:key",
++ "file": "server/files/routes.js",
++ "line": 156
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/pastes",
++ "file": "server/pastes/routes.js",
++ "line": 248
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/pastes",
++ "file": "server/pastes/routes.js",
++ "line": 462
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/v1/:app/pastes/:slug",
++ "file": "server/pastes/routes.js",
++ "line": 616
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/pastes/:slug",
++ "file": "server/pastes/routes.js",
++ "line": 436
++ },
++ {
++ "method": "PUT",
++ "path": "/api/v1/:app/pastes/:slug",
++ "file": "server/pastes/routes.js",
++ "line": 578
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/pastes/:slug/ai",
++ "file": "server/pastes/routes.js",
++ "line": 558
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/pastes/:slug/censor",
++ "file": "server/pastes/routes.js",
++ "line": 397
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/pastes/:slug/comments",
++ "file": "server/pastes/routes.js",
++ "line": 702
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/pastes/:slug/comments",
++ "file": "server/pastes/routes.js",
++ "line": 725
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/v1/:app/pastes/:slug/comments/:commentId",
++ "file": "server/pastes/routes.js",
++ "line": 803
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/pastes/:slug/copy",
++ "file": "server/pastes/routes.js",
++ "line": 677
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/pastes/:slug/fork",
++ "file": "server/pastes/routes.js",
++ "line": 634
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/pastes/:slug/like",
++ "file": "server/pastes/routes.js",
++ "line": 660
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/v1/:app/pastes/admin/forks",
++ "file": "server/pastes/routes.js",
++ "line": 359
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/pastes/admin/forks",
++ "file": "server/pastes/routes.js",
++ "line": 339
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/pastes/admin/stats",
++ "file": "server/pastes/routes.js",
++ "line": 329
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/pastes/bulk",
++ "file": "server/pastes/routes.js",
++ "line": 372
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/pastes/config",
++ "file": "server/pastes/routes.js",
++ "line": 307
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/stats",
++ "file": "server/index.js",
++ "line": 93
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/stats/series/:metric",
++ "file": "server/index.js",
++ "line": 103
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/thumbnails/:kind/:id",
++ "file": "server/thumbnails/routes.js",
++ "line": 38
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/views",
++ "file": "server/views/routes.js",
++ "line": 39
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/views",
++ "file": "server/views/routes.js",
++ "line": 24
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/vods",
++ "file": "server/vod/routes.js",
++ "line": 266
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/vods",
++ "file": "server/vod/routes.js",
++ "line": 89
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/v1/:app/vods/:id",
++ "file": "server/vod/routes.js",
++ "line": 353
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/vods/:id",
++ "file": "server/vod/routes.js",
++ "line": 305
++ },
++ {
++ "method": "PUT",
++ "path": "/api/v1/:app/vods/:id",
++ "file": "server/vod/routes.js",
++ "line": 331
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/vods/:id/chunks",
++ "file": "server/vod/routes.js",
++ "line": 155
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/vods/:id/complete",
++ "file": "server/vod/routes.js",
++ "line": 259
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/vods/:id/finalize",
++ "file": "server/vod/routes.js",
++ "line": 260
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/vods/:id/ingest/rtmp",
++ "file": "server/vod/routes.js",
++ "line": 112
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/vods/:id/ingest/rtp/start",
++ "file": "server/vod/routes.js",
++ "line": 126
++ },
++ {
++ "method": "POST",
++ "path": "/api/v1/:app/vods/:id/ingest/rtp/stop",
++ "file": "server/vod/routes.js",
++ "line": 140
++ },
++ {
++ "method": "GET",
++ "path": "/api/v1/:app/vods/latest-thumbs",
++ "file": "server/vod/routes.js",
++ "line": 294
++ },
++ {
++ "method": "GET",
++ "path": "/auth/callback",
++ "file": "server/user-auth.js",
++ "line": 281
++ },
++ {
++ "method": "POST",
++ "path": "/auth/fedcm",
++ "file": "server/user-auth.js",
++ "line": 327
++ },
++ {
++ "method": "GET",
++ "path": "/auth/login",
++ "file": "server/user-auth.js",
++ "line": 259
++ },
++ {
++ "method": "GET",
++ "path": "/auth/logout",
++ "file": "server/user-auth.js",
++ "line": 355
++ },
++ {
++ "method": "GET",
++ "path": "/auth/me",
++ "file": "server/user-auth.js",
++ "line": 379
++ },
++ {
++ "method": "POST",
++ "path": "/auth/refresh",
++ "file": "server/user-auth.js",
++ "line": 389
++ },
++ {
++ "method": "GET",
++ "path": "/healthz",
++ "file": "server/index.js",
++ "line": 82
++ },
++ {
++ "method": "POST",
++ "path": "/internal/avatar-ingest",
++ "file": "server/index.js",
++ "line": 56
++ },
++ {
++ "method": "GET",
++ "path": "/manifest.webmanifest",
++ "file": "server/index.js",
++ "line": 44
++ },
++ {
++ "method": "GET",
++ "path": "/tos",
++ "file": "server/index.js",
++ "line": 41
++ }
++ ],
++ "ws": {
++ "servers": [],
++ "paths": []
++ },
++ "env": {
++ "HOST": [
++ "server/config.js"
++ ],
++ "NODE_ENV": [
++ "server/config.js"
++ ],
++ "MEDIA_PUBLIC_URL": [
++ "server/config.js"
++ ],
++ "DB_PATH": [
++ "server/config.js"
++ ],
++ "VOD_PATH": [
++ "server/config.js"
++ ],
++ "CLIPS_PATH": [
++ "server/config.js"
++ ],
++ "PASTES_PATH": [
++ "server/config.js"
++ ],
++ "ASSETS_PATH": [
++ "server/config.js"
++ ],
++ "THUMBNAILS_PATH": [
++ "server/config.js"
++ ],
++ "FILES_PATH": [
++ "server/config.js"
++ ],
++ "OV_NETWORK_URL": [
++ "server/config.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_NETWORK_INTERNAL_URL": [
++ "server/config.js"
++ ],
++ "INTERNAL_API_KEY": [
++ "server/config.js",
++ "server/views/service.js"
++ ],
++ "MEDIA_APPS_SEED": [
++ "server/config.js"
++ ],
++ "MEDIA_APP_KEYS": [
++ "server/config.js"
++ ],
++ "OV_OAUTH_CLIENT_ID": [
++ "server/index.js"
++ ],
++ "OV_OAUTH_CLIENT_SECRET": [
++ "server/index.js"
++ ],
++ "OV_OAUTH_REDIRECT_URI": [
++ "server/index.js"
++ ],
++ "APP_INTERNAL_URLS": [
++ "server/thumbnails/live-frame-service.js"
++ ],
++ "LIVE_APP_INTERNAL_URL": [
++ "server/thumbnails/live-frame-service.js"
++ ],
++ "VIEW_HASH_SECRET": [
++ "server/views/service.js"
++ ],
++ "MEDIA_SECRET": [
++ "server/views/service.js"
++ ],
++ "CLIP_MAX_CONCURRENT_FFMPEG": [
++ "server/vod/clip-cutter.js"
++ ],
++ "CLIP_QUEUE_WAIT_MS": [
++ "server/vod/clip-cutter.js"
++ ],
++ "MIN_VOD_SECONDS": [
++ "server/vod/finalize.js"
++ ],
++ "VOD_STOP_GRACE_MS": [
++ "server/vod/recorder.js"
++ ],
++ "VOD_DISK_WARN_GB": [
++ "server/vod/recorder.js"
++ ],
++ "VOD_DISK_CRIT_GB": [
++ "server/vod/recorder.js"
++ ],
++ "VOD_NO_NORMALIZE": [
++ "server/vod/recorder.js"
++ ],
++ "MEDIA_B2_ENDPOINT": [
++ "server/vod/vod-storage.js"
++ ],
++ "MEDIA_B2_REGION": [
++ "server/vod/vod-storage.js"
++ ],
++ "MEDIA_B2_BUCKET": [
++ "server/vod/vod-storage.js"
++ ],
++ "MEDIA_B2_KEY_ID": [
++ "server/vod/vod-storage.js"
++ ],
++ "MEDIA_B2_APP_KEY": [
++ "server/vod/vod-storage.js"
++ ],
++ "MEDIA_R2_ENDPOINT": [
++ "server/vod/vod-storage.js"
++ ],
++ "MEDIA_R2_REGION": [
++ "server/vod/vod-storage.js"
++ ],
++ "MEDIA_R2_BUCKET": [
++ "server/vod/vod-storage.js"
++ ],
++ "MEDIA_R2_ACCESS_KEY_ID": [
++ "server/vod/vod-storage.js"
++ ],
++ "MEDIA_R2_SECRET_ACCESS_KEY": [
++ "server/vod/vod-storage.js"
++ ],
++ "LOGIN_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_NETWORK_LOGIN_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_TOOLS_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_LIVE_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_GAMES_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_MEDIA_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_COMMUNITY_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_MAPS_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_FOOD_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_IMG_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_YT_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_AUDIO_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_TEXT_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_LOGO_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_DOCS_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_NET_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_DEV_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_PASTES_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ]
++ },
++ "outbound": [
++ {
++ "file": "server/auth.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "network.url",
++ "line": 28
++ },
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 28
++ },
++ {
++ "via": "network.url",
++ "line": 83
++ }
++ ]
++ },
++ {
++ "service": "webhook",
++ "refs": [
++ {
++ "via": "webhook_url (per-app, configured in DB)",
++ "line": 268
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "none-detected"
++ ],
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "file": "server/thumbnails/live-frame-service.js",
++ "targets": [
++ {
++ "service": "live",
++ "refs": [
++ {
++ "via": ":3000",
++ "line": 170
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "none-detected"
++ ],
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "file": "server/user-auth.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 49
++ },
++ {
++ "via": "networkUrl",
++ "line": 57
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 58
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 67
++ },
++ {
++ "via": "networkUrl",
++ "line": 67
++ },
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 70
++ },
++ {
++ "via": "networkUrl",
++ "line": 146
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 227
++ },
++ {
++ "via": "networkUrl",
++ "line": 227
++ },
++ {
++ "via": "/oauth/token",
++ "line": 230
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 361
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "oauth-client"
++ ],
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "file": "server/webhooks.js",
++ "targets": [
++ {
++ "service": "webhook",
++ "refs": [
++ {
++ "via": "webhook_url (per-app, configured in DB)",
++ "line": 4
++ },
++ {
++ "via": "webhook_url (per-app, configured in DB)",
++ "line": 38
++ },
++ {
++ "via": "webhook_url (per-app, configured in DB)",
++ "line": 48
++ },
++ {
++ "via": "webhook_url (per-app, configured in DB)",
++ "line": 55
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "hmac"
++ ],
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "file": "vendor/openvibe-shared/account-switcher.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 176
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "file": "vendor/openvibe-shared/auth-client.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": ":4000",
++ "line": 23
++ },
++ {
++ "via": ":4000",
++ "line": 31
++ },
++ {
++ "via": "/oauth/token",
++ "line": 60
++ },
++ {
++ "via": "/oauth/token",
++ "line": 84
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key",
++ "oauth-client"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "vendor/openvibe-shared/footer.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 280
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "vendor/openvibe-shared/navbar.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 10
++ },
++ {
++ "via": "/api/auth/me",
++ "line": 998
++ },
++ {
++ "via": "/api/auth/me",
++ "line": 1001
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": true
++ }
++ ],
++ "jobs": [
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 58,
++ "file": "server/auth.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 43200000,
++ "line": 115,
++ "file": "server/index.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 141,
++ "file": "server/index.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 694,
++ "file": "server/pastes/routes.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 111,
++ "file": "server/vod/clip-cutter.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 117,
++ "file": "server/vod/clip-jobs.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 262,
++ "file": "server/vod/health-job.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 60000,
++ "line": 213,
++ "file": "server/vod/recorder.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 466,
++ "file": "server/vod/vod-storage.js"
++ }
++ ],
++ "contractTodos": 0,
++ "unresolvedMounts": [],
++ "testFiles": 4,
++ "ci": false
++ },
++ {
++ "name": "OpenVibe.Tools",
++ "service": "tools",
++ "present": true,
++ "git": {
++ "head": "0346edc57fb9f588abcdea171a22a5ca33f772d2",
++ "branch": "main",
++ "originMain": "0346edc57fb9f588abcdea171a22a5ca33f772d2",
++ "committedAt": "2026-09-21T09:58:34-07:00"
++ },
++ "sourceFiles": 117,
++ "loc": 20018,
++ "tables": [
++ {
++ "table": "analytics_events",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 74,
++ "via": "vendored openvibe-shared"
++ },
++ {
++ "table": "analytics_hourly",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 98,
++ "via": "vendored openvibe-shared"
++ },
++ {
++ "table": "analytics_daily",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 116,
++ "via": "vendored openvibe-shared"
++ },
++ {
++ "table": "analytics_rate_tracking",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 137,
++ "via": "vendored openvibe-shared"
++ }
++ ],
++ "routes": [
++ {
++ "method": "GET",
++ "path": "/",
++ "file": "apps/gateway/server/pages/site.js",
++ "line": 249
++ },
++ {
++ "method": "GET",
++ "path": "/",
++ "file": "apps/text/server/index.js",
++ "line": 194
++ },
++ {
++ "method": "GET",
++ "path": "/:slug",
++ "file": "apps/gateway/server/pages/site.js",
++ "line": 257
++ },
++ {
++ "method": "GET",
++ "path": "/all-tools",
++ "file": "apps/gateway/server/pages/site.js",
++ "line": 250
++ },
++ {
++ "method": "GET",
++ "path": "/api/brand",
++ "file": "apps/gateway/server/index.js",
++ "line": 132
++ },
++ {
++ "method": "GET",
++ "path": "/api/catalog.json",
++ "file": "apps/gateway/server/pages/site.js",
++ "line": 248
++ },
++ {
++ "method": "GET",
++ "path": "/api/context",
++ "file": "apps/audio/server/index.js",
++ "line": 97
++ },
++ {
++ "method": "GET",
++ "path": "/api/context",
++ "file": "apps/docs/server/index.js",
++ "line": 94
++ },
++ {
++ "method": "GET",
++ "path": "/api/context",
++ "file": "apps/img/server/index.js",
++ "line": 95
++ },
++ {
++ "method": "GET",
++ "path": "/api/dev/opengraph",
++ "file": "apps/gateway/server/dev/routes.js",
++ "line": 91
++ },
++ {
++ "method": "GET",
++ "path": "/api/dev/tools",
++ "file": "apps/gateway/server/dev/routes.js",
++ "line": 79
++ },
++ {
++ "method": "POST",
++ "path": "/api/dev/webhook/bins",
++ "file": "apps/gateway/server/dev/routes.js",
++ "line": 151
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/dev/webhook/bins/:binId",
++ "file": "apps/gateway/server/dev/routes.js",
++ "line": 217
++ },
++ {
++ "method": "GET",
++ "path": "/api/dev/webhook/bins/:binId",
++ "file": "apps/gateway/server/dev/routes.js",
++ "line": 172
++ },
++ {
++ "method": "ALL",
++ "path": "/api/dev/webhook/bins/:binId/in",
++ "file": "apps/gateway/server/dev/routes.js",
++ "line": 186
++ },
++ {
++ "method": "POST",
++ "path": "/api/download",
++ "file": "apps/yt/server/index.js",
++ "line": 121
++ },
++ {
++ "method": "DELETE",
++ "path": "/api/download/:id",
++ "file": "apps/yt/server/index.js",
++ "line": 202
++ },
++ {
++ "method": "GET",
++ "path": "/api/download/:id",
++ "file": "apps/audio/server/index.js",
++ "line": 195
++ },
++ {
++ "method": "GET",
++ "path": "/api/download/:id",
++ "file": "apps/docs/server/index.js",
++ "line": 236
++ },
++ {
++ "method": "GET",
++ "path": "/api/download/:id",
++ "file": "apps/img/server/index.js",
++ "line": 209
++ },
++ {
++ "method": "GET",
++ "path": "/api/download/:id",
++ "file": "apps/yt/server/index.js",
++ "line": 209
++ },
++ {
++ "method": "GET",
++ "path": "/api/food-banks",
++ "file": "apps/food/server/index.js",
++ "line": 81
++ },
++ {
++ "method": "GET",
++ "path": "/api/food-banks",
++ "file": "apps/maps/server/index.js",
++ "line": 374
++ },
++ {
++ "method": "GET",
++ "path": "/api/foods",
++ "file": "apps/food/server/index.js",
++ "line": 83
++ },
++ {
++ "method": "GET",
++ "path": "/api/foods",
++ "file": "apps/maps/server/index.js",
++ "line": 395
++ },
++ {
++ "method": "GET",
++ "path": "/api/geocode",
++ "file": "apps/food/server/index.js",
++ "line": 85
++ },
++ {
++ "method": "GET",
++ "path": "/api/geocode",
++ "file": "apps/maps/server/index.js",
++ "line": 97
++ },
++ {
++ "method": "GET",
++ "path": "/api/health",
++ "file": "apps/audio/server/index.js",
++ "line": 91
++ },
++ {
++ "method": "GET",
++ "path": "/api/health",
++ "file": "apps/docs/server/index.js",
++ "line": 88
++ },
++ {
++ "method": "GET",
++ "path": "/api/health",
++ "file": "apps/gateway/server/index.js",
++ "line": 128
++ },
++ {
++ "method": "GET",
++ "path": "/api/health",
++ "file": "apps/img/server/index.js",
++ "line": 89
++ },
++ {
++ "method": "GET",
++ "path": "/api/health",
++ "file": "apps/text/server/index.js",
++ "line": 167
++ },
++ {
++ "method": "GET",
++ "path": "/api/health",
++ "file": "apps/yt/server/index.js",
++ "line": 99
++ },
++ {
++ "method": "POST",
++ "path": "/api/info",
++ "file": "apps/docs/server/index.js",
++ "line": 116
++ },
++ {
++ "method": "POST",
++ "path": "/api/info",
++ "file": "apps/yt/server/index.js",
++ "line": 106
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics",
++ "file": "apps/audio/server/index.js",
++ "line": 244
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics",
++ "file": "apps/docs/server/index.js",
++ "line": 254
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics",
++ "file": "apps/food/server/index.js",
++ "line": 88
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics",
++ "file": "apps/gateway/server/index.js",
++ "line": 220
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics",
++ "file": "apps/img/server/index.js",
++ "line": 227
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics",
++ "file": "apps/maps/server/index.js",
++ "line": 417
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics",
++ "file": "apps/text/server/index.js",
++ "line": 155
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics",
++ "file": "apps/yt/server/index.js",
++ "line": 223
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics/bots",
++ "file": "apps/audio/server/index.js",
++ "line": 249
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics/bots",
++ "file": "apps/docs/server/index.js",
++ "line": 259
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics/bots",
++ "file": "apps/food/server/index.js",
++ "line": 93
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics/bots",
++ "file": "apps/img/server/index.js",
++ "line": 232
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics/bots",
++ "file": "apps/maps/server/index.js",
++ "line": 422
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics/bots",
++ "file": "apps/text/server/index.js",
++ "line": 160
++ },
++ {
++ "method": "GET",
++ "path": "/api/internal/analytics/bots",
++ "file": "apps/yt/server/index.js",
++ "line": 228
++ },
++ {
++ "method": "GET",
++ "path": "/api/meal-plan",
++ "file": "apps/food/server/index.js",
++ "line": 84
++ },
++ {
++ "method": "GET",
++ "path": "/api/meal-plan",
++ "file": "apps/maps/server/index.js",
++ "line": 405
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/dns/:target?",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 273
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/headers/:target?",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 489
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/ip/:target?",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 97
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/ipv4/:target?",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 151
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/ipv6/:target?",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 225
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/lookup/:target?",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 695
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/myip",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 90
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/ping/:target?",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 639
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/port/:target?",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 590
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/rdap/:target?",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 361
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/rdns/:target?",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 333
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/redirects/:target?",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 539
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/ssl/:target?",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 409
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/tools",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 82
++ },
++ {
++ "method": "GET",
++ "path": "/api/net/whois/:target?",
++ "file": "apps/gateway/server/net/routes.js",
++ "line": 398
++ },
++ {
++ "method": "GET",
++ "path": "/api/preview/:id",
++ "file": "apps/audio/server/index.js",
++ "line": 213
++ },
++ {
++ "method": "POST",
++ "path": "/api/probe",
++ "file": "apps/audio/server/index.js",
++ "line": 119
++ },
++ {
++ "method": "POST",
++ "path": "/api/process",
++ "file": "apps/audio/server/index.js",
++ "line": 132
++ },
++ {
++ "method": "POST",
++ "path": "/api/process",
++ "file": "apps/docs/server/index.js",
++ "line": 128
++ },
++ {
++ "method": "POST",
++ "path": "/api/process",
++ "file": "apps/img/server/index.js",
++ "line": 117
++ },
++ {
++ "method": "POST",
++ "path": "/api/process/direct",
++ "file": "apps/img/server/index.js",
++ "line": 174
++ },
++ {
++ "method": "POST",
++ "path": "/api/process/multi",
++ "file": "apps/docs/server/index.js",
++ "line": 189
++ },
++ {
++ "method": "GET",
++ "path": "/api/search",
++ "file": "apps/maps/server/index.js",
++ "line": 203
++ },
++ {
++ "method": "GET",
++ "path": "/api/search/stream",
++ "file": "apps/maps/server/index.js",
++ "line": 248
++ },
++ {
++ "method": "GET",
++ "path": "/api/status/:id",
++ "file": "apps/yt/server/index.js",
++ "line": 136
++ },
++ {
++ "method": "GET",
++ "path": "/api/status/:id/stream",
++ "file": "apps/yt/server/index.js",
++ "line": 152
++ },
++ {
++ "method": "GET",
++ "path": "/api/stores",
++ "file": "apps/food/server/index.js",
++ "line": 82
++ },
++ {
++ "method": "GET",
++ "path": "/api/stores",
++ "file": "apps/maps/server/index.js",
++ "line": 385
++ },
++ {
++ "method": "GET",
++ "path": "/api/terrain",
++ "file": "apps/maps/server/index.js",
++ "line": 364
++ },
++ {
++ "method": "GET",
++ "path": "/api/tools",
++ "file": "apps/audio/server/index.js",
++ "line": 114
++ },
++ {
++ "method": "GET",
++ "path": "/api/tools",
++ "file": "apps/docs/server/index.js",
++ "line": 111
++ },
++ {
++ "method": "GET",
++ "path": "/api/tools",
++ "file": "apps/img/server/index.js",
++ "line": 112
++ },
++ {
++ "method": "GET",
++ "path": "/api/weather",
++ "file": "apps/maps/server/index.js",
++ "line": 348
++ },
++ {
++ "method": "GET",
++ "path": "/auth/fedcm/callback",
++ "file": "apps/gateway/server/auth/routes.js",
++ "line": 331
++ },
++ {
++ "method": "POST",
++ "path": "/auth/fedcm/fedcm",
++ "file": "apps/gateway/server/auth/routes.js",
++ "line": 381
++ },
++ {
++ "method": "GET",
++ "path": "/auth/fedcm/login",
++ "file": "apps/gateway/server/auth/routes.js",
++ "line": 303
++ },
++ {
++ "method": "GET",
++ "path": "/auth/fedcm/logout",
++ "file": "apps/gateway/server/auth/routes.js",
++ "line": 431
++ },
++ {
++ "method": "GET",
++ "path": "/auth/fedcm/me",
++ "file": "apps/gateway/server/auth/routes.js",
++ "line": 458
++ },
++ {
++ "method": "POST",
++ "path": "/auth/fedcm/refresh",
++ "file": "apps/gateway/server/auth/routes.js",
++ "line": 469
++ },
++ {
++ "method": "GET",
++ "path": "/llms.txt",
++ "file": "apps/gateway/server/pages/site.js",
++ "line": 255
++ },
++ {
++ "method": "GET",
++ "path": "/openvibe-sw.js",
++ "file": "apps/gateway/server/index.js",
++ "line": 285
++ },
++ {
++ "method": "GET",
++ "path": "/robots.txt",
++ "file": "apps/gateway/server/pages/site.js",
++ "line": 254
++ },
++ {
++ "method": "GET",
++ "path": "/search",
++ "file": "apps/gateway/server/pages/site.js",
++ "line": 251
++ },
++ {
++ "method": "GET",
++ "path": "/sitemap.xml",
++ "file": "apps/gateway/server/pages/site.js",
++ "line": 253
++ },
++ {
++ "method": "GET",
++ "path": "/sitemap.xml",
++ "file": "apps/text/server/index.js",
++ "line": 188
++ },
++ {
++ "method": "GET",
++ "path": "/tool/:id",
++ "file": "apps/gateway/server/pages/site.js",
++ "line": 252
++ },
++ {
++ "method": "GET",
++ "path": "/tos",
++ "file": "apps/gateway/server/index.js",
++ "line": 245
++ }
++ ],
++ "ws": {
++ "servers": [],
++ "paths": []
++ },
++ "env": {
++ "OV_EXTRA_HOSTS": [
++ "apps/_shared/host-role.js"
++ ],
++ "INTERNAL_API_KEY": [
++ "apps/_shared/internal-auth.js"
++ ],
++ "OV_INTERNAL_KEY": [
++ "apps/_shared/internal-auth.js"
++ ],
++ "PORT": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/food/server/index.js",
++ "apps/gateway/server/config.js",
++ "apps/img/server/config.js",
++ "apps/maps/server/config.js",
++ "apps/text/server/config.js",
++ "apps/yt/server/config.js"
++ ],
++ "HOST": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/gateway/server/config.js",
++ "apps/img/server/config.js",
++ "apps/maps/server/config.js",
++ "apps/text/server/config.js",
++ "apps/yt/server/config.js"
++ ],
++ "DATA_DIR": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/img/server/config.js",
++ "apps/yt/server/config.js"
++ ],
++ "UPLOADS_DIR": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/img/server/config.js"
++ ],
++ "OUTPUT_DIR": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/img/server/config.js"
++ ],
++ "OV_NETWORK_URL": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/gateway/server/config.js",
++ "apps/img/server/config.js",
++ "apps/text/server/config.js",
++ "apps/yt/server/config.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_NETWORK_INTERNAL_URL": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/gateway/server/config.js",
++ "apps/img/server/config.js",
++ "apps/yt/server/config.js"
++ ],
++ "OV_NETWORK_PUBLIC_KEY": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/img/server/config.js",
++ "apps/yt/server/config.js"
++ ],
++ "NODE_ENV": [
++ "apps/audio/server/index.js",
++ "apps/docs/server/index.js",
++ "apps/food/server/index.js",
++ "apps/gateway/server/config.js",
++ "apps/img/server/index.js",
++ "apps/text/server/index.js",
++ "apps/yt/server/index.js",
++ "apps/yt/server/seo.js"
++ ],
++ "MAPS_API": [
++ "apps/food/server/index.js"
++ ],
++ "BASE_URL": [
++ "apps/gateway/server/config.js",
++ "apps/maps/server/config.js",
++ "apps/text/server/config.js"
++ ],
++ "OV_OAUTH_CLIENT_ID": [
++ "apps/gateway/server/config.js"
++ ],
++ "OV_OAUTH_CLIENT_SECRET": [
++ "apps/gateway/server/config.js"
++ ],
++ "OV_OAUTH_REDIRECT_URI": [
++ "apps/gateway/server/config.js"
++ ],
++ "COOKIE_DOMAIN": [
++ "apps/gateway/server/config.js"
++ ],
++ "COOKIE_SECURE": [
++ "apps/gateway/server/config.js"
++ ],
++ "LIVE_URL": [
++ "apps/gateway/server/config.js"
++ ],
++ "OV_ENFORCE_HOSTS": [
++ "apps/gateway/server/index.js"
++ ],
++ "OV_COMMUNITY_URL": [
++ "apps/gateway/server/index.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_DOMAINS_URL": [
++ "apps/gateway/server/registry/index.js"
++ ],
++ "RIDB_API_KEY": [
++ "apps/maps/server/config.js",
++ "apps/maps/server/sources/ridb.js"
++ ],
++ "NPS_API_KEY": [
++ "apps/maps/server/config.js"
++ ],
++ "OPEN_CHARGE_MAP_KEY": [
++ "apps/maps/server/config.js"
++ ],
++ "YTDLP_PATH": [
++ "apps/yt/server/config.js"
++ ],
++ "DOWNLOADS_DIR": [
++ "apps/yt/server/config.js"
++ ],
++ "YT_PROXY": [
++ "apps/yt/server/downloader.js"
++ ],
++ "YT_COOKIES_FILE": [
++ "apps/yt/server/downloader.js"
++ ],
++ "LOGIN_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_NETWORK_LOGIN_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_TOOLS_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_LIVE_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_GAMES_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_MEDIA_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_MAPS_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_FOOD_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_IMG_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_YT_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_AUDIO_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_TEXT_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_LOGO_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_DOCS_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_NET_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_DEV_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_PASTES_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ]
++ },
++ "outbound": [
++ {
++ "file": "apps/audio/server/auth.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 9
++ },
++ {
++ "via": "networkUrl",
++ "line": 20
++ },
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 21
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 40
++ },
++ {
++ "via": "networkUrl",
++ "line": 40
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "none-detected"
++ ],
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "file": "apps/docs/server/auth.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 9
++ },
++ {
++ "via": "networkUrl",
++ "line": 20
++ },
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 21
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 40
++ },
++ {
++ "via": "networkUrl",
++ "line": 40
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "none-detected"
++ ],
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "file": "apps/gateway/server/auth/routes.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/oauth/token",
++ "line": 53
++ },
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 122
++ },
++ {
++ "via": "networkUrl",
++ "line": 130
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 131
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 140
++ },
++ {
++ "via": "networkUrl",
++ "line": 140
++ },
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 143
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 271
++ },
++ {
++ "via": "networkUrl",
++ "line": 271
++ },
++ {
++ "via": "/oauth/token",
++ "line": 274
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 438
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "oauth-client"
++ ],
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "file": "apps/gateway/server/index.js",
++ "targets": [
++ {
++ "service": "live",
++ "refs": [
++ {
++ "via": "liveUrl",
++ "line": 148
++ },
++ {
++ "via": "liveUrl",
++ "line": 318
++ }
++ ]
++ },
++ {
++ "service": "community",
++ "refs": [
++ {
++ "via": "OV_COMMUNITY_URL",
++ "line": 266
++ }
++ ]
++ },
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "networkUrl",
++ "line": 317
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key",
++ "bearer"
++ ],
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "file": "apps/img/server/auth.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 9
++ },
++ {
++ "via": "networkUrl",
++ "line": 20
++ },
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 21
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 40
++ },
++ {
++ "via": "networkUrl",
++ "line": 40
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "none-detected"
++ ],
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "file": "apps/yt/server/auth.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 9
++ },
++ {
++ "via": "networkUrl",
++ "line": 20
++ },
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 21
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 40
++ },
++ {
++ "via": "networkUrl",
++ "line": 40
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "none-detected"
++ ],
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "file": "vendor/openvibe-shared/account-switcher.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 176
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "file": "vendor/openvibe-shared/auth-client.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": ":4000",
++ "line": 23
++ },
++ {
++ "via": ":4000",
++ "line": 31
++ },
++ {
++ "via": "/oauth/token",
++ "line": 60
++ },
++ {
++ "via": "/oauth/token",
++ "line": 84
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key",
++ "oauth-client"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "vendor/openvibe-shared/footer.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 280
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "vendor/openvibe-shared/navbar.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 10
++ },
++ {
++ "via": "/api/auth/me",
++ "line": 998
++ },
++ {
++ "via": "/api/auth/me",
++ "line": 1001
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": true
++ }
++ ],
++ "jobs": [
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 60,
++ "file": "apps/audio/server/auth.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 193,
++ "file": "apps/audio/server/retention/manager.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 60,
++ "file": "apps/docs/server/auth.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 145,
++ "file": "apps/docs/server/retention/manager.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 300000,
++ "line": 25,
++ "file": "apps/gateway/server/dev/routes.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 60000,
++ "line": 191,
++ "file": "apps/gateway/server/registry/index.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 60,
++ "file": "apps/img/server/auth.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 146,
++ "file": "apps/img/server/retention/manager.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 60,
++ "file": "apps/yt/server/auth.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 555,
++ "file": "apps/yt/server/downloader.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 5000,
++ "line": 606,
++ "file": "apps/yt/server/downloader.js"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": 500,
++ "line": 196,
++ "file": "apps/yt/server/index.js"
++ }
++ ],
++ "contractTodos": 0,
++ "unresolvedMounts": [],
++ "testFiles": 2,
++ "ci": false
++ },
++ {
++ "name": "OpenVibe.Community",
++ "service": "community",
++ "present": true,
++ "git": {
++ "head": "de947ead2addce29d7f9b98f3d894075ca4e82f2",
++ "branch": "main",
++ "originMain": "de947ead2addce29d7f9b98f3d894075ca4e82f2",
++ "committedAt": "2026-09-21T09:58:34-07:00"
++ },
++ "sourceFiles": 11,
++ "loc": 1754,
++ "tables": [
++ {
++ "table": "analytics_events",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 74,
++ "via": "vendored openvibe-shared"
++ },
++ {
++ "table": "analytics_hourly",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 98,
++ "via": "vendored openvibe-shared"
++ },
++ {
++ "table": "analytics_daily",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 116,
++ "via": "vendored openvibe-shared"
++ },
++ {
++ "table": "analytics_rate_tracking",
++ "file": "vendor/openvibe-shared/analytics.js",
++ "line": 137,
++ "via": "vendored openvibe-shared"
++ }
++ ],
++ "routes": [
++ {
++ "method": "GET",
++ "path": "/",
++ "file": "server/app.js",
++ "line": 106
++ },
++ {
++ "method": "GET",
++ "path": "/api/health",
++ "file": "server/app.js",
++ "line": 81
++ },
++ {
++ "method": "GET",
++ "path": "/api/ready",
++ "file": "server/app.js",
++ "line": 82
++ },
++ {
++ "method": "GET",
++ "path": "/callback",
++ "file": "server/auth/routes.js",
++ "line": 281
++ },
++ {
++ "method": "POST",
++ "path": "/fedcm",
++ "file": "server/auth/routes.js",
++ "line": 327
++ },
++ {
++ "method": "GET",
++ "path": "/feed.xml",
++ "file": "server/app.js",
++ "line": 99
++ },
++ {
++ "method": "GET",
++ "path": "/login",
++ "file": "server/auth/routes.js",
++ "line": 259
++ },
++ {
++ "method": "GET",
++ "path": "/logout",
++ "file": "server/auth/routes.js",
++ "line": 355
++ },
++ {
++ "method": "GET",
++ "path": "/me",
++ "file": "server/auth/routes.js",
++ "line": 379
++ },
++ {
++ "method": "GET",
++ "path": "/my",
++ "file": "server/app.js",
++ "line": 186
++ },
++ {
++ "method": "GET",
++ "path": "/new",
++ "file": "server/app.js",
++ "line": 150
++ },
++ {
++ "method": "POST",
++ "path": "/new",
++ "file": "server/app.js",
++ "line": 159
++ },
++ {
++ "method": "GET",
++ "path": "/p/:slug",
++ "file": "server/app.js",
++ "line": 119
++ },
++ {
++ "method": "GET",
++ "path": "/p/:slug/download",
++ "file": "server/app.js",
++ "line": 138
++ },
++ {
++ "method": "GET",
++ "path": "/p/:slug/raw",
++ "file": "server/app.js",
++ "line": 135
++ },
++ {
++ "method": "GET",
++ "path": "/p/:slug/screenshot",
++ "file": "server/app.js",
++ "line": 136
++ },
++ {
++ "method": "GET",
++ "path": "/pastes",
++ "file": "server/app.js",
++ "line": 111
++ },
++ {
++ "method": "GET",
++ "path": "/pastes/:slug",
++ "file": "server/app.js",
++ "line": 117
++ },
++ {
++ "method": "POST",
++ "path": "/refresh",
++ "file": "server/auth/routes.js",
++ "line": 389
++ },
++ {
++ "method": "GET",
++ "path": "/robots.txt",
++ "file": "server/app.js",
++ "line": 97
++ },
++ {
++ "method": "GET",
++ "path": "/sitemap.xml",
++ "file": "server/app.js",
++ "line": 98
++ },
++ {
++ "method": "GET",
++ "path": "/tos",
++ "file": "server/app.js",
++ "line": 75
++ }
++ ],
++ "ws": {
++ "servers": [],
++ "paths": []
++ },
++ "env": {
++ "NODE_ENV": [
++ "server/config.js"
++ ],
++ "PORT": [
++ "server/config.js"
++ ],
++ "HOST": [
++ "server/config.js"
++ ],
++ "BASE_URL": [
++ "server/config.js"
++ ],
++ "TRUST_PROXY": [
++ "server/config.js"
++ ],
++ "OV_NETWORK_URL": [
++ "server/config.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_NETWORK_INTERNAL_URL": [
++ "server/config.js"
++ ],
++ "OV_OAUTH_CLIENT_ID": [
++ "server/config.js"
++ ],
++ "OV_OAUTH_CLIENT_SECRET": [
++ "server/config.js"
++ ],
++ "OV_OAUTH_REDIRECT_URI": [
++ "server/config.js"
++ ],
++ "COOKIE_SECURE": [
++ "server/config.js"
++ ],
++ "OV_LIVE_INTERNAL_URL": [
++ "server/config.js"
++ ],
++ "OV_LIVE_URL": [
++ "server/config.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_MEDIA_URL": [
++ "server/config.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "LOGIN_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_NETWORK_LOGIN_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_TOOLS_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_GAMES_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_COMMUNITY_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_MAPS_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_FOOD_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_IMG_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_YT_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_AUDIO_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_TEXT_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_LOGO_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_DOCS_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_NET_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_DEV_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "OV_PASTES_URL": [
++ "vendor/openvibe-shared/brand.js"
++ ]
++ },
++ "outbound": [
++ {
++ "file": "server/auth/routes.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 49
++ },
++ {
++ "via": "networkUrl",
++ "line": 57
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 58
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 67
++ },
++ {
++ "via": "networkUrl",
++ "line": 67
++ },
++ {
++ "via": "/api/.well-known/jwks",
++ "line": 70
++ },
++ {
++ "via": "networkUrl",
++ "line": 146
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 227
++ },
++ {
++ "via": "networkUrl",
++ "line": 227
++ },
++ {
++ "via": "/oauth/token",
++ "line": 230
++ },
++ {
++ "via": "networkInternalUrl",
++ "line": 361
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "oauth-client"
++ ],
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "file": "server/live-client.js",
++ "targets": [
++ {
++ "service": "live",
++ "refs": [
++ {
++ "via": "liveInternalUrl",
++ "line": 6
++ },
++ {
++ "via": "liveInternalUrl",
++ "line": 37
++ }
++ ]
++ },
++ {
++ "service": "media",
++ "refs": [
++ {
++ "via": "mediaUrl",
++ "line": 86
++ },
++ {
++ "via": "mediaUrl",
++ "line": 87
++ },
++ {
++ "via": "mediaUrl",
++ "line": 92
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "file": "server/pastes/proxy.js",
++ "targets": [
++ {
++ "service": "live",
++ "refs": [
++ {
++ "via": "liveUrl",
++ "line": 26
++ },
++ {
++ "via": "liveInternalUrl",
++ "line": 26
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "file": "vendor/openvibe-shared/account-switcher.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 176
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "file": "vendor/openvibe-shared/auth-client.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": ":4000",
++ "line": 23
++ },
++ {
++ "via": ":4000",
++ "line": 31
++ },
++ {
++ "via": "/oauth/token",
++ "line": 60
++ },
++ {
++ "via": "/oauth/token",
++ "line": 84
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "internal-key",
++ "oauth-client"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "vendor/openvibe-shared/footer.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 280
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "file": "vendor/openvibe-shared/navbar.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 10
++ },
++ {
++ "via": "/api/auth/me",
++ "line": 998
++ },
++ {
++ "via": "/api/auth/me",
++ "line": 1001
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": true
++ }
++ ],
++ "jobs": [],
++ "contractTodos": 0,
++ "unresolvedMounts": [],
++ "testFiles": 8,
++ "ci": false
++ },
++ {
++ "name": "OpenVibe.Games",
++ "service": "games",
++ "present": true,
++ "git": {
++ "head": "571028796639c00d5deecca038634ef1c52d3480",
++ "branch": "main",
++ "originMain": "571028796639c00d5deecca038634ef1c52d3480",
++ "committedAt": "2026-09-18T04:02:57-07:00"
++ },
++ "sourceFiles": 101,
++ "loc": 18511,
++ "tables": [
++ {
++ "table": "world_entities",
++ "file": "packages/persistence/src/sqlite/sqliteStore.ts",
++ "line": 25,
++ "via": "repo"
++ },
++ {
++ "table": "players",
++ "file": "packages/persistence/src/sqlite/sqliteStore.ts",
++ "line": 36,
++ "via": "repo"
++ },
++ {
++ "table": "constraints",
++ "file": "packages/persistence/src/sqlite/sqliteStore.ts",
++ "line": 55,
++ "via": "repo"
++ },
++ {
++ "table": "guest_ips",
++ "file": "packages/persistence/src/sqlite/sqliteStore.ts",
++ "line": 63,
++ "via": "repo"
++ },
++ {
++ "table": "meta",
++ "file": "packages/persistence/src/sqlite/sqliteStore.ts",
++ "line": 67,
++ "via": "repo"
++ },
++ {
++ "table": "constraints",
++ "file": "packages/persistence/src/sqlite/sqliteStore.ts",
++ "line": 78,
++ "via": "repo"
++ },
++ {
++ "table": "players",
++ "file": "packages/persistence/src/sqlite/sqliteStore.ts",
++ "line": 101,
++ "via": "repo"
++ },
++ {
++ "table": "guest_ips",
++ "file": "packages/persistence/src/sqlite/sqliteStore.ts",
++ "line": 123,
++ "via": "repo"
++ }
++ ],
++ "routes": [
++ {
++ "method": "ANY",
++ "path": "/",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 616
++ },
++ {
++ "method": "ANY",
++ "path": "/api/characters",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 302
++ },
++ {
++ "method": "POST",
++ "path": "/api/map",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 151
++ },
++ {
++ "method": "POST",
++ "path": "/api/map-assets",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 221
++ },
++ {
++ "method": "POST",
++ "path": "/api/texture",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 221
++ },
++ {
++ "method": "ANY",
++ "path": "/auth/*",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 322
++ },
++ {
++ "method": "ANY",
++ "path": "/auth/callback",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 388
++ },
++ {
++ "method": "ANY",
++ "path": "/auth/fedcm",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 474
++ },
++ {
++ "method": "ANY",
++ "path": "/auth/login",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 337
++ },
++ {
++ "method": "ANY",
++ "path": "/auth/logout",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 549
++ },
++ {
++ "method": "ANY",
++ "path": "/auth/me",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 566
++ },
++ {
++ "method": "ANY",
++ "path": "/editor",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 620
++ },
++ {
++ "method": "ANY",
++ "path": "/editor.html",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 603
++ },
++ {
++ "method": "ANY",
++ "path": "/healthz",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 584
++ },
++ {
++ "method": "ANY",
++ "path": "/map-assets/*",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 276
++ },
++ {
++ "method": "ANY",
++ "path": "/map.json",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 138
++ },
++ {
++ "method": "ANY",
++ "path": "/metrics",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 589
++ },
++ {
++ "method": "ANY",
++ "path": "/play",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 610
++ },
++ {
++ "method": "ANY",
++ "path": "/play",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 618
++ },
++ {
++ "method": "ANY",
++ "path": "/play.html",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 603
++ },
++ {
++ "method": "ANY",
++ "path": "/play.html",
++ "file": "apps/server/src/net/httpServer.ts",
++ "line": 605
++ }
++ ],
++ "ws": {
++ "servers": [
++ {
++ "file": "apps/server/src/net/editorWs.ts",
++ "line": 53
++ },
++ {
++ "file": "apps/server/src/net/wsTransport.ts",
++ "line": 19
++ }
++ ],
++ "paths": [
++ {
++ "path": "/ws",
++ "file": "apps/server/src/main.ts",
++ "line": 129
++ }
++ ]
++ },
++ "env": {
++ "HOST": [
++ "apps/server/src/config.ts"
++ ],
++ "DB_PATH": [
++ "apps/server/src/config.ts"
++ ],
++ "STATIC_DIR": [
++ "apps/server/src/config.ts"
++ ],
++ "MAP_PATH": [
++ "apps/server/src/config.ts"
++ ],
++ "EDITOR_KEY": [
++ "apps/server/src/config.ts"
++ ],
++ "OV_NETWORK_AUTH_URL": [
++ "apps/server/src/config.ts"
++ ],
++ "OV_OAUTH_CLIENT_SECRET": [
++ "apps/server/src/config.ts"
++ ],
++ "OV_NETWORK_URL": [
++ "apps/server/src/config.ts"
++ ],
++ "GUEST_IP_BINDING": [
++ "apps/server/src/config.ts"
++ ],
++ "OV_OAUTH_CLIENT_ID": [
++ "apps/server/src/config.ts"
++ ],
++ "SELF_URL": [
++ "apps/server/src/config.ts"
++ ],
++ "PLAY_URL": [
++ "apps/server/src/config.ts"
++ ],
++ "EVENT_INTERVAL_SCALE": [
++ "apps/server/src/config.ts"
++ ],
++ "LOG_LEVEL": [
++ "apps/server/src/main.ts"
++ ],
++ "OWNER_USERNAME": [
++ "apps/server/src/net/networkAuth.ts"
++ ]
++ },
++ "outbound": [
++ {
++ "file": "apps/server/src/net/httpServer.ts",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 335
++ },
++ {
++ "via": "/api/auth/me",
++ "line": 367
++ },
++ {
++ "via": "/oauth/token",
++ "line": 432
++ },
++ {
++ "via": "/oauth/token",
++ "line": 511
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "oauth-client"
++ ],
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "file": "apps/server/src/net/networkAuth.ts",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 3
++ },
++ {
++ "via": "/api/auth/me",
++ "line": 18
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": false
++ }
++ ],
++ "jobs": [
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 159,
++ "file": "apps/server/src/main.ts"
++ },
++ {
++ "kind": "setInterval",
++ "name": null,
++ "intervalMs": null,
++ "line": 82,
++ "file": "apps/server/src/net/editorWs.ts"
++ }
++ ],
++ "contractTodos": 0,
++ "unresolvedMounts": [],
++ "testFiles": 56,
++ "ci": false
++ },
++ {
++ "name": "OpenVibe.Sites",
++ "service": "sites",
++ "present": true,
++ "git": {
++ "head": "185014ff01ed8bd8f6df3dd5099e2b6c904e235d",
++ "branch": "main",
++ "originMain": "185014ff01ed8bd8f6df3dd5099e2b6c904e235d",
++ "committedAt": "2026-09-21T09:22:26-07:00"
++ },
++ "sourceFiles": 1,
++ "loc": 306,
++ "tables": [],
++ "routes": [],
++ "ws": {
++ "servers": [],
++ "paths": []
++ },
++ "env": {},
++ "outbound": [
++ {
++ "file": "vendor/openvibe-shared/footer.js",
++ "targets": [
++ {
++ "service": "network",
++ "refs": [
++ {
++ "via": "/api/auth/me",
++ "line": 280
++ }
++ ]
++ }
++ ],
++ "auth": [
++ "bearer"
++ ],
++ "timeout": false,
++ "retry": false
++ }
++ ],
++ "jobs": [],
++ "contractTodos": 0,
++ "unresolvedMounts": [],
++ "testFiles": 0,
++ "ci": false
++ }
++ ],
++ "vendored": [
++ {
++ "repo": "OpenVibe.Community",
++ "file": "account-switcher.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "analytics.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "app-icon.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "auth-client.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "brand.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "builtin-themes.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "chrome-ssr.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "footer.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "history.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "index.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "island.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "legal.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "middleware.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "navbar.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "notification-ui.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "notifications.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "openvibe-sw.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "ov-icons.js",
++ "state": "divergent"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "ov-mark.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "panels.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "scripts/build-app-icons.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "scripts/build-theme-loader.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "scripts/measure-icons.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "seo.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "sso-client.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "theme-loader.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "theme-sync.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "tooltip.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "ui.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "url-resolver.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "file": "user-card.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "account-switcher.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "analytics.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "app-icon.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "auth-client.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "brand.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "builtin-themes.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "chrome-ssr.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "footer.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "history.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "index.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "island.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "legal.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "middleware.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "navbar.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "notification-ui.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "notifications.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "openvibe-sw.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "ov-icons.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "ov-mark.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "panels.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "scripts/build-app-icons.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "scripts/build-theme-loader.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "scripts/measure-icons.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "seo.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "sso-client.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "theme-loader.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "theme-sync.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "tooltip.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "ui.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "url-resolver.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "file": "user-card.js",
++ "state": "divergent"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "account-switcher.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "analytics.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "app-icon.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "auth-client.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "brand.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "builtin-themes.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "chrome-ssr.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "footer.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "history.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "index.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "island.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "legal.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "middleware.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "navbar.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "notification-ui.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "notifications.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "openvibe-sw.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "ov-icons.js",
++ "state": "divergent"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "ov-mark.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "panels.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "scripts/build-app-icons.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "scripts/build-theme-loader.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "scripts/measure-icons.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "seo.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "sso-client.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "theme-loader.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "theme-sync.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "tooltip.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "ui.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "url-resolver.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "file": "user-card.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Sites",
++ "file": "app-icon.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Sites",
++ "file": "chrome-ssr.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Sites",
++ "file": "footer.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Sites",
++ "file": "legal.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Sites",
++ "file": "seo.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "account-switcher.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "analytics.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "app-icon.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "auth-client.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "brand.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "builtin-themes.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "chrome-ssr.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "footer.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "history.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "index.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "island.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "legal.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "middleware.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "navbar.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "notification-ui.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "notifications.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "openvibe-sw.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "ov-icons.js",
++ "state": "divergent"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "ov-mark.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "panels.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "scripts/build-app-icons.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "scripts/build-theme-loader.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "scripts/measure-icons.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "seo.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "sso-client.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "theme-loader.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "theme-sync.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "tooltip.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "ui.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "url-resolver.js",
++ "state": "identical"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "file": "user-card.js",
++ "state": "identical"
++ }
++ ],
++ "ownership": [
++ {
++ "repo": "OpenVibe.Community",
++ "table": "analytics_daily",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:116"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "target": "OpenVibe.Community",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "table": "analytics_events",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:74"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "target": "OpenVibe.Community",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "table": "analytics_hourly",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:98"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "target": "OpenVibe.Community",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "table": "analytics_rate_tracking",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:137"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "target": "OpenVibe.Community",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "table": "constraints",
++ "declaredAt": [
++ "packages/persistence/src/sqlite/sqliteStore.ts:55",
++ "packages/persistence/src/sqlite/sqliteStore.ts:78"
++ ],
++ "prodDbs": [
++ "world.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Games",
++ "disposition": "keep",
++ "wave": "W12",
++ "note": "Authoritative game state."
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "table": "guest_ips",
++ "declaredAt": [
++ "packages/persistence/src/sqlite/sqliteStore.ts:63",
++ "packages/persistence/src/sqlite/sqliteStore.ts:123"
++ ],
++ "prodDbs": [
++ "world.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Games",
++ "disposition": "keep",
++ "wave": "W12",
++ "note": "Authoritative game state."
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "table": "meta",
++ "declaredAt": [
++ "packages/persistence/src/sqlite/sqliteStore.ts:67"
++ ],
++ "prodDbs": [
++ "world.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Games",
++ "disposition": "keep",
++ "wave": "W12",
++ "note": "Authoritative game state."
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "table": "players",
++ "declaredAt": [
++ "packages/persistence/src/sqlite/sqliteStore.ts:36",
++ "packages/persistence/src/sqlite/sqliteStore.ts:101"
++ ],
++ "prodDbs": [
++ "world.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Games",
++ "disposition": "keep",
++ "wave": "W12",
++ "note": "Authoritative game state."
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "table": "world_entities",
++ "declaredAt": [
++ "packages/persistence/src/sqlite/sqliteStore.ts:25"
++ ],
++ "prodDbs": [
++ "world.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Games",
++ "disposition": "keep",
++ "wave": "W12",
++ "note": "Authoritative game state."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "active_effects",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "ai_chatbot_configs",
++ "declaredAt": [
++ "server/db/database.js:1803"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "AI runs, caches and derived artifacts; products keep the published projections."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "ai_conversations",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "ai_memory",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "ai_timeline_cache",
++ "declaredAt": [
++ "server/db/database.js:1296"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "AI runs, caches and derived artifacts; products keep the published projections."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "ai_usage",
++ "declaredAt": [
++ "server/db/database.js:1254"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "AI runs, caches and derived artifacts; products keep the published projections."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "ai_viewer_log",
++ "declaredAt": [
++ "server/db/database.js:1887"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "AI runs, caches and derived artifacts; products keep the published projections."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "ai_viewer_threads",
++ "declaredAt": [
++ "server/db/database.js:1873"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "AI runs, caches and derived artifacts; products keep the published projections."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "analytics_daily",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:116"
++ ],
++ "prodDbs": [
++ "analytics.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "analytics_events",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:74"
++ ],
++ "prodDbs": [
++ "analytics.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "analytics_hourly",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:98"
++ ],
++ "prodDbs": [
++ "analytics.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "analytics_rate_tracking",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:137"
++ ],
++ "prodDbs": [
++ "analytics.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "analytics_snapshots",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "anon_ip_mappings",
++ "declaredAt": [
++ "server/db/database.js:936"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "api_keys",
++ "declaredAt": [
++ "server/db/schema.sql:391"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "api_tokens",
++ "declaredAt": [
++ "server/db/schema.sql:404"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "app_state",
++ "declaredAt": [
++ "server/db/database.js:1167"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Service-local config/bookkeeping."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "approved_ips",
++ "declaredAt": [
++ "server/db/database.js:2075"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "arena_achievements",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_battles",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_beef_hype",
++ "declaredAt": [
++ "server/arena/beef.js:58"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product feature."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_beef_sides",
++ "declaredAt": [
++ "server/arena/beef.js:68"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product feature."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_beefs",
++ "declaredAt": [
++ "server/arena/beef.js:34"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product feature."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_events",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_mic_moments",
++ "declaredAt": [
++ "server/arena/mic.js:54"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product feature."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_profiles",
++ "declaredAt": [
++ "server/arena/arena-service.js:82"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product feature."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_talk",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_talk_hype",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_talk_session_hype",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_talk_session_topics",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_talk_sessions",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_talk_topics",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_tier_paid",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_topic_hype",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_topic_members",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_topic_moments",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_topic_progress",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_topic_sides",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_topic_threads",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_topics",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_trash_levels",
++ "declaredAt": [
++ "server/arena/mic.js:31"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product feature."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_viewer_clout",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_votes",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "arena_xp_log",
++ "declaredAt": [
++ "server/arena/mic.js:45"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product feature."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "bank_items",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "bans",
++ "declaredAt": [
++ "server/db/schema.sql:418"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "battle_stats",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "camera_presets",
++ "declaredAt": [
++ "server/db/schema.sql:359"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "camera_profiles",
++ "declaredAt": [
++ "server/db/schema.sql:339"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "cameras",
++ "declaredAt": [
++ "server/db/schema.sql:133"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "canvas_actions",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "canvas_bans",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "canvas_region_locks",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "canvas_settings",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "canvas_snapshots",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "canvas_tiles",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "canvas_user_overrides",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "changelog_entries",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "channel_ai_bots",
++ "declaredAt": [
++ "server/db/database.js:1828"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "AI runs, caches and derived artifacts; products keep the published projections."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "channel_ai_config",
++ "declaredAt": [
++ "server/db/database.js:1852"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "AI runs, caches and derived artifacts; products keep the published projections."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "channel_moderation_settings",
++ "declaredAt": [
++ "server/db/database.js:1638",
++ "server/db/schema.sql:514"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "channel_moderators",
++ "declaredAt": [
++ "server/db/database.js:1621",
++ "server/db/schema.sql:499"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "channel_points",
++ "declaredAt": [
++ "server/db/database.js:760"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Billing (loyalty ledger)",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "Loyalty/progression, never money. Classification blocks migration."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "channel_sounds",
++ "declaredAt": [
++ "server/db/database.js:1741",
++ "server/db/schema.sql:625"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "channel_sounds_new",
++ "declaredAt": [
++ "server/db/database.js:1765"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "channels",
++ "declaredAt": [
++ "server/db/schema.sql:26"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product data and read models."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "chat_ai_summaries",
++ "declaredAt": [
++ "server/db/database.js:1308"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "chat_log",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "chat_messages",
++ "declaredAt": [
++ "server/db/schema.sql:148"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "chat_messages_new",
++ "declaredAt": [
++ "server/db/database.js:415"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "chat_timeline_events",
++ "declaredAt": [
++ "server/db/database.js:1328"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "chatter_profiles",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "chatter_subjects",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "chatter_xp_log",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "target": "archive then drop",
++ "disposition": "investigate",
++ "wave": "W0",
++ "note": "Left behind by the Arena v1/v2 -> v3 rewrite (4ea1a7c); no code reads them."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "clip_ai_state",
++ "declaredAt": [
++ "server/db/database.js:272"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "Live-owned AI state for Media-hosted content."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "clips",
++ "declaredAt": [
++ "server/db/schema.sql:307"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Media",
++ "disposition": "frozen-legacy",
++ "wave": "W4",
++ "note": "Frozen for the Media cutover (AGENTS.md: never write)."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "coin_redemptions",
++ "declaredAt": [
++ "server/db/schema.sql:689"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Billing (loyalty ledger)",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "Loyalty/progression, never money. Classification blocks migration."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "coin_rewards",
++ "declaredAt": [
++ "server/db/schema.sql:668"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Billing (loyalty ledger)",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "Loyalty/progression, never money. Classification blocks migration."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "coin_transactions",
++ "declaredAt": [
++ "server/db/schema.sql:651"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Billing (loyalty ledger)",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "Loyalty/progression, never money. Classification blocks migration."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "comments",
++ "declaredAt": [
++ "server/db/database.js:849",
++ "server/db/schema.sql:585"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Community",
++ "disposition": "extract",
++ "wave": "W5",
++ "note": "Typed reusable comment threads."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "content_views",
++ "declaredAt": [
++ "server/db/schema.sql:328"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product data and read models."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "control_config_buttons",
++ "declaredAt": [
++ "server/db/database.js:2306"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "control_configs",
++ "declaredAt": [
++ "server/db/database.js:2292"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "control_whitelist",
++ "declaredAt": [
++ "server/db/database.js:2277"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "custom_rewards",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "daily_claims",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "divorce_history",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "dm_blocks",
++ "declaredAt": [
++ "server/chat/dm.js:57"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "dm_conversations",
++ "declaredAt": [
++ "server/chat/dm.js:21"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "dm_messages",
++ "declaredAt": [
++ "server/chat/dm.js:42"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "dm_participants",
++ "declaredAt": [
++ "server/chat/dm.js:31"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "donation_goals",
++ "declaredAt": [
++ "server/db/schema.sql:264"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Tips",
++ "disposition": "extract",
++ "wave": "W9",
++ "note": ""
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "dungeon_stats",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "easter_egg_solves",
++ "declaredAt": [
++ "server/db/database.js:1340"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "AI runs, caches and derived artifacts; products keep the published projections."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "emotes",
++ "declaredAt": [
++ "server/db/schema.sql:603"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "emotes_new",
++ "declaredAt": [
++ "server/db/database.js:1709"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "event_log",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "farm_plots",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "file_checksums",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "fish_profile",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "follows",
++ "declaredAt": [
++ "server/db/schema.sql:221"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "extract",
++ "wave": "W1",
++ "note": "Social graph consolidates in Network."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "game_achievements",
++ "declaredAt": [
++ "server/game/game-engine.js:536"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_bank",
++ "declaredAt": [
++ "server/game/schema.sql:77"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_battle_stats",
++ "declaredAt": [
++ "server/game/schema.sql:142"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_daily_quest_claims",
++ "declaredAt": [
++ "server/game/game-engine.js:557",
++ "server/game/schema.sql:203"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_daily_quest_progress",
++ "declaredAt": [
++ "server/game/game-engine.js:546",
++ "server/game/schema.sql:192"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_dungeon_runs",
++ "declaredAt": [
++ "server/game/schema.sql:156"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_effects",
++ "declaredAt": [
++ "server/game/schema.sql:128"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_farm_plots",
++ "declaredAt": [
++ "server/game/schema.sql:104"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_fish_collection",
++ "declaredAt": [
++ "server/game/game-engine.js:522",
++ "server/game/schema.sql:180"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_inventory",
++ "declaredAt": [
++ "server/game/schema.sql:66"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_leaderboard",
++ "declaredAt": [
++ "server/game/schema.sql:168"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_players",
++ "declaredAt": [
++ "server/game/schema.sql:13"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_recipes",
++ "declaredAt": [
++ "server/game/schema.sql:118"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_structures",
++ "declaredAt": [
++ "server/game/schema.sql:87"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "game_world_state",
++ "declaredAt": [
++ "server/game/schema.sql:7"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "gusher_profile",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "hidden_relay_users",
++ "declaredAt": [
++ "server/db/database.js:2112"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "inventory",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "ip_log",
++ "declaredAt": [
++ "server/db/database.js:2051"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "jo_profile",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "kick_channel_cache",
++ "declaredAt": [
++ "server/db/database.js:774"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "linked_accounts",
++ "declaredAt": [
++ "server/db/database.js:384"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "extract",
++ "wave": "W1",
++ "note": "Identity truth moves to Network subjects; Live keeps a subject projection + channel state."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "love_affinity",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "love_profile",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "love_proposals",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "managed_streams",
++ "declaredAt": [
++ "server/db/database.js:2420",
++ "server/db/schema.sql:78"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "media_request_settings",
++ "declaredAt": [
++ "server/db/database.js:869",
++ "server/db/schema.sql:722"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "media_requests",
++ "declaredAt": [
++ "server/db/database.js:883",
++ "server/db/schema.sql:739"
++ ],
++ "prodDbs": [
++ "live.db",
++ "rs-companion.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "mine_profile",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "mod_persuasion",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "moderation_actions",
++ "declaredAt": [
++ "server/db/database.js:1920"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "music_profile",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "news_settings",
++ "declaredAt": [
++ "server/news/news-service.js:47"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product data and read models."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "paste_comments",
++ "declaredAt": [
++ "server/db/database.js:2008"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Community",
++ "disposition": "frozen-legacy",
++ "wave": "W5",
++ "note": "Frozen; Community must own paste semantics."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "paste_likes",
++ "declaredAt": [
++ "server/db/database.js:1976"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Community",
++ "disposition": "frozen-legacy",
++ "wave": "W5",
++ "note": "Frozen; Community must own paste semantics."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "pastes",
++ "declaredAt": [
++ "server/db/database.js:1939"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Community",
++ "disposition": "frozen-legacy",
++ "wave": "W5",
++ "note": "Frozen; Community must own paste semantics."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "payment_orders",
++ "declaredAt": [
++ "server/db/database.js:1588"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Billing",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "Real-money Vibes/PayPal records."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "pending_ip_messages",
++ "declaredAt": [
++ "server/db/database.js:2092"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "platform_connections",
++ "declaredAt": [
++ "server/db/database.js:709"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "powerchat_connections",
++ "declaredAt": [
++ "server/db/database.js:731"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "W3 (deliveries -> Events)",
++ "note": "External PowerChat integration."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "powerchat_webhook_deliveries",
++ "declaredAt": [
++ "server/db/database.js:748"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "W3 (deliveries -> Events)",
++ "note": "External PowerChat integration."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "promo_claims",
++ "declaredAt": [],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "relay_users",
++ "declaredAt": [
++ "server/db/database.js:1466"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "restream_destinations",
++ "declaredAt": [
++ "server/db/database.js:691"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "roast_dossier",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "roast_log",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "robotstreamer_integrations",
++ "declaredAt": [
++ "server/db/database.js:605",
++ "server/db/schema.sql:58"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "robotstreamer_integrations_new",
++ "declaredAt": [
++ "server/db/database.js:656"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "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",
++ "table": "schema_migrations",
++ "declaredAt": [
++ "server/db/migrations.js:27"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Service-local config/bookkeeping."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "site_settings",
++ "declaredAt": [
++ "server/db/database.js:353",
++ "server/db/schema.sql:556"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Service-local config/bookkeeping."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "stream_analytics",
++ "declaredAt": [
++ "server/db/schema.sql:810"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product data and read models."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "stream_controls",
++ "declaredAt": [
++ "server/db/schema.sql:372"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "stream_controls_new",
++ "declaredAt": [
++ "server/db/database.js:2385"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "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",
++ "table": "stream_first_chats",
++ "declaredAt": [
++ "server/db/database.js:2040"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "stream_memories",
++ "declaredAt": [
++ "server/db/database.js:1181"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "AI runs, caches and derived artifacts; products keep the published projections."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "stream_recaps",
++ "declaredAt": [
++ "server/recap/recap.js:27"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "AI runs, caches and derived artifacts; products keep the published projections."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "stream_timeline_events",
++ "declaredAt": [
++ "server/db/database.js:1229"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product data and read models."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "streamer_overviews",
++ "declaredAt": [
++ "server/db/database.js:1274"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "AI runs, caches and derived artifacts; products keep the published projections."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "streams",
++ "declaredAt": [
++ "server/db/schema.sql:103"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product data and read models."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "subscriptions",
++ "declaredAt": [
++ "server/db/schema.sql:234"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Billing + OpenVibe.VIP",
++ "disposition": "extract",
++ "wave": "W8/W10",
++ "note": "Billing owns the contract/entitlement, VIP the plan/perk UX."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "tag_guardian_defeats",
++ "declaredAt": [
++ "server/game/tags.js:81"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product feature."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "themes",
++ "declaredAt": [
++ "server/db/schema.sql:446"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "extract",
++ "wave": "W1",
++ "note": "Identity truth moves to Network subjects; Live keeps a subject projection + channel state."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "transactions",
++ "declaredAt": [
++ "server/db/schema.sql:247"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Billing",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "Real-money Vibes/PayPal records."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "translations",
++ "declaredAt": [
++ "server/db/database.js:506",
++ "server/i18n/translate.js:145"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "AI runs, caches and derived artifacts; products keep the published projections."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "tts_voice_overrides",
++ "declaredAt": [
++ "server/db/database.js:1352"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "unlocked_recipes",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "user_cosmetics",
++ "declaredAt": [
++ "server/db/schema.sql:774",
++ "server/monetization/cosmetics.js:109"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Billing (loyalty ledger)",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "Loyalty/progression, never money. Classification blocks migration."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "user_equipped",
++ "declaredAt": [
++ "server/db/schema.sql:786",
++ "server/monetization/cosmetics.js:118"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Billing (loyalty ledger)",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "Loyalty/progression, never money. Classification blocks migration."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "user_equipped_tag",
++ "declaredAt": [
++ "server/game/tags.js:76"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Billing (loyalty ledger)",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "Loyalty/progression, never money. Classification blocks migration."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "user_preferences",
++ "declaredAt": [
++ "server/db/schema.sql:825"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "extract",
++ "wave": "W1",
++ "note": "Identity truth moves to Network subjects; Live keeps a subject projection + channel state."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "user_tags",
++ "declaredAt": [
++ "server/game/tags.js:67"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Chat",
++ "disposition": "extract",
++ "wave": "W6",
++ "note": "Rooms, messages, DMs, TTS/audio queues, chat moderation."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "user_themes",
++ "declaredAt": [
++ "server/db/schema.sql:467"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "extract",
++ "wave": "W1",
++ "note": "Identity truth moves to Network subjects; Live keeps a subject projection + channel state."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "user_voice_selection",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "user_voices",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Live",
++ "table": "users",
++ "declaredAt": [
++ "server/db/schema.sql:5"
++ ],
++ "prodDbs": [
++ "live.db",
++ "rs-companion.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "extract",
++ "wave": "W1",
++ "note": "Identity truth moves to Network subjects; Live keeps a subject projection + channel state."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "verification_keys",
++ "declaredAt": [
++ "server/db/database.js:364",
++ "server/db/schema.sql:567"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "extract",
++ "wave": "W1",
++ "note": "Identity truth moves to Network subjects; Live keeps a subject projection + channel state."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "vibe_coding_events",
++ "declaredAt": [
++ "server/db/database.js:2677",
++ "server/db/schema.sql:198"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Codes",
++ "disposition": "extract",
++ "wave": "W20",
++ "note": "Scoped vibe-coding publication (roadmap 15.5)."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "vibe_coding_sessions",
++ "declaredAt": [
++ "server/db/database.js:2642",
++ "server/db/schema.sql:170"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Codes",
++ "disposition": "extract",
++ "wave": "W20",
++ "note": "Scoped vibe-coding publication (roadmap 15.5)."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "viewer_samples",
++ "declaredAt": [
++ "server/db/database.js:3956"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product data and read models."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "viewer_snapshots",
++ "declaredAt": [
++ "server/db/schema.sql:799"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product data and read models."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "vod_ai_state",
++ "declaredAt": [
++ "server/db/database.js:263"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.AI",
++ "disposition": "extract",
++ "wave": "W13",
++ "note": "Live-owned AI state for Media-hosted content."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "vods",
++ "declaredAt": [
++ "server/db/schema.sql:280"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Media",
++ "disposition": "frozen-legacy",
++ "wave": "W4",
++ "note": "Frozen for the Media cutover (AGENTS.md: never write)."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "vpn_approvals",
++ "declaredAt": [
++ "server/db/schema.sql:434"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "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",
++ "table": "watch_time",
++ "declaredAt": [
++ "server/db/schema.sql:706"
++ ],
++ "prodDbs": [
++ "live.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Live",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Live product data and read models."
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "table": "woodcut_profile",
++ "declaredAt": [],
++ "prodDbs": [
++ "rs-companion.db"
++ ],
++ "inCode": false,
++ "inProd": true,
++ "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": "OpenVibe.Media",
++ "table": "analytics_daily",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:116"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "target": "OpenVibe.Media",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "analytics_events",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:74"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "target": "OpenVibe.Media",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "analytics_hourly",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:98"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "target": "OpenVibe.Media",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "analytics_rate_tracking",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:137"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "target": "OpenVibe.Media",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "apps",
++ "declaredAt": [
++ "server/db/schema.sql:8"
++ ],
++ "prodDbs": [
++ "media.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Media",
++ "disposition": "keep",
++ "wave": "W4 (object model)",
++ "note": "Becomes projections over media_objects/media_locations."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "assets",
++ "declaredAt": [
++ "server/db/schema.sql:189"
++ ],
++ "prodDbs": [
++ "media.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Media",
++ "disposition": "keep",
++ "wave": "W4 (object model)",
++ "note": "Becomes projections over media_objects/media_locations."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "clips",
++ "declaredAt": [
++ "server/db/schema.sql:63"
++ ],
++ "prodDbs": [
++ "media.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Media",
++ "disposition": "keep",
++ "wave": "W4 (object model)",
++ "note": "Becomes projections over media_objects/media_locations."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "content_views",
++ "declaredAt": [
++ "server/db/schema.sql:94"
++ ],
++ "prodDbs": [
++ "media.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Media",
++ "disposition": "keep",
++ "wave": "W4 (object model)",
++ "note": "Becomes projections over media_objects/media_locations."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "content_visits",
++ "declaredAt": [
++ "server/views/service.js:43"
++ ],
++ "prodDbs": [
++ "media.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Media",
++ "disposition": "keep",
++ "wave": "W4 (object model)",
++ "note": "Becomes projections over media_objects/media_locations."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "files",
++ "declaredAt": [
++ "server/db/schema.sql:164"
++ ],
++ "prodDbs": [
++ "media.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Media",
++ "disposition": "keep",
++ "wave": "W4 (object model)",
++ "note": "Becomes projections over media_objects/media_locations."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "media_settings",
++ "declaredAt": [
++ "server/db/schema.sql:178"
++ ],
++ "prodDbs": [
++ "media.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Media",
++ "disposition": "keep",
++ "wave": "W4 (object model)",
++ "note": "Becomes projections over media_objects/media_locations."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "paste_comments",
++ "declaredAt": [
++ "server/db/schema.sql:146"
++ ],
++ "prodDbs": [
++ "media.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Community",
++ "disposition": "extract",
++ "wave": "W5",
++ "note": "Media keeps bytes (screenshots/raw); semantics move to Community."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "paste_likes",
++ "declaredAt": [
++ "server/db/schema.sql:138"
++ ],
++ "prodDbs": [
++ "media.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Community",
++ "disposition": "extract",
++ "wave": "W5",
++ "note": "Media keeps bytes (screenshots/raw); semantics move to Community."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "pastes",
++ "declaredAt": [
++ "server/db/schema.sql:105"
++ ],
++ "prodDbs": [
++ "media.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Community",
++ "disposition": "extract",
++ "wave": "W5",
++ "note": "Media keeps bytes (screenshots/raw); semantics move to Community."
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "table": "vods",
++ "declaredAt": [
++ "server/db/schema.sql:22"
++ ],
++ "prodDbs": [
++ "media.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Media",
++ "disposition": "keep",
++ "wave": "W4 (object model)",
++ "note": "Becomes projections over media_objects/media_locations."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "admin_rate_limits",
++ "declaredAt": [
++ "server/auth/admin-limits.js:24"
++ ],
++ "prodDbs": [],
++ "inCode": true,
++ "inProd": false,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "analytics_daily",
++ "declaredAt": [
++ "packages/openvibe-shared/analytics.js:116"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "analytics_events",
++ "declaredAt": [
++ "packages/openvibe-shared/analytics.js:74"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "analytics_hourly",
++ "declaredAt": [
++ "packages/openvibe-shared/analytics.js:98"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "analytics_rate_tracking",
++ "declaredAt": [
++ "packages/openvibe-shared/analytics.js:137"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "anon_ip_log",
++ "declaredAt": [
++ "server/db/database.js:443"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "anon_users",
++ "declaredAt": [
++ "server/db/database.js:340"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "audit_log",
++ "declaredAt": [
++ "server/db/database.js:215"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "chrome_cache",
++ "declaredAt": [
++ "server/chrome/service.js:27"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "chrome_hits",
++ "declaredAt": [
++ "server/chrome/service.js:29"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "coin_transactions",
++ "declaredAt": [
++ "server/db/database.js:407"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Billing (loyalty ledger)",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "OpenCoins are earned loyalty, not money."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "email_delivery_log",
++ "declaredAt": [
++ "server/db/database.js:302"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W3 (delivery -> Events outbox)",
++ "note": "ADR-020: Network keeps addressing/authority."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "email_verification_tokens",
++ "declaredAt": [
++ "server/db/database.js:506"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "follows",
++ "declaredAt": [
++ "server/db/database.js:386"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "ip_log",
++ "declaredAt": [
++ "server/db/database.js:225"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "linked_accounts",
++ "declaredAt": [
++ "server/db/database.js:204"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "notification_preferences",
++ "declaredAt": [
++ "server/db/database.js:290"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "notifications",
++ "declaredAt": [
++ "server/db/database.js:263"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "oauth_clients",
++ "declaredAt": [
++ "server/db/database.js:134"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "oauth_codes",
++ "declaredAt": [
++ "server/db/database.js:144"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "oauth_tokens",
++ "declaredAt": [
++ "server/db/database.js:158"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "password_reset_tokens",
++ "declaredAt": [
++ "server/db/database.js:321"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "push_subscriptions",
++ "declaredAt": [
++ "server/db/database.js:460"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W3 (delivery -> Events outbox)",
++ "note": "ADR-020: Network keeps addressing/authority."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "site_settings",
++ "declaredAt": [
++ "server/db/database.js:238"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "-",
++ "note": "Service-local config/bookkeeping."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "stream_live_announcements",
++ "declaredAt": [
++ "server/internal/routes.js:300"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Events",
++ "disposition": "extract",
++ "wave": "W3",
++ "note": "Go-live fanout rate state; durable delivery moves to Events."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "themes",
++ "declaredAt": [
++ "server/db/database.js:183"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "tool_domains",
++ "declaredAt": [
++ "server/domains/routes.js:44"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "url_registry",
++ "declaredAt": [
++ "server/db/database.js:245",
++ "server/url-registry.js:7"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "user_effects",
++ "declaredAt": [
++ "server/db/database.js:375"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "user_history",
++ "declaredAt": [
++ "server/history/routes.js:26"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "user_preferences",
++ "declaredAt": [
++ "server/db/database.js:172"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "user_sessions",
++ "declaredAt": [
++ "server/db/database.js:354"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "users",
++ "declaredAt": [
++ "server/db/database.js:113"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "verification_keys",
++ "declaredAt": [
++ "server/db/database.js:422"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Network",
++ "disposition": "keep",
++ "wave": "W1 (module split)",
++ "note": "Identity, sessions, OAuth, themes, notifications, registry, chrome, audit, admin."
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "table": "wallets",
++ "declaredAt": [
++ "server/db/database.js:400"
++ ],
++ "prodDbs": [
++ "network.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Billing (loyalty ledger)",
++ "disposition": "extract",
++ "wave": "W8 (after ADR-012)",
++ "note": "OpenCoins are earned loyalty, not money."
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "table": "analytics_daily",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:116"
++ ],
++ "prodDbs": [
++ "analytics.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Tools",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "table": "analytics_events",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:74"
++ ],
++ "prodDbs": [
++ "analytics.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Tools",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "table": "analytics_hourly",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:98"
++ ],
++ "prodDbs": [
++ "analytics.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Tools",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "table": "analytics_rate_tracking",
++ "declaredAt": [
++ "vendor/openvibe-shared/analytics.js:137"
++ ],
++ "prodDbs": [
++ "analytics.db"
++ ],
++ "inCode": true,
++ "inProd": true,
++ "target": "OpenVibe.Tools",
++ "disposition": "keep",
++ "wave": "Track O / ADR-021",
++ "note": "Per-site analytics from vendored openvibe-shared/analytics.js. Raw analytics_events has no pruning (hazard H11)."
++ }
++ ],
++ "calls": [
++ {
++ "caller": "OpenVibe.Community",
++ "file": "server/live-client.js",
++ "target": "OpenVibe.Live",
++ "via": "liveInternalUrl",
++ "lines": "6,37",
++ "auth": "bearer",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Community",
++ "file": "server/pastes/proxy.js",
++ "target": "OpenVibe.Live",
++ "via": "liveUrl, liveInternalUrl",
++ "lines": "26,26",
++ "auth": "bearer",
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Community",
++ "file": "server/live-client.js",
++ "target": "OpenVibe.Media",
++ "via": "mediaUrl",
++ "lines": "86,87,92",
++ "auth": "bearer",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Community",
++ "file": "server/auth/routes.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/.well-known/jwks, networkUrl, networkInternalUrl, /oauth/token",
++ "lines": "49,57,58,67,67,70,146,227,227,230,361",
++ "auth": "oauth-client",
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Community",
++ "file": "vendor/openvibe-shared/account-switcher.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "176",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Community",
++ "file": "vendor/openvibe-shared/auth-client.js",
++ "target": "OpenVibe.Network",
++ "via": ":4000, /oauth/token",
++ "lines": "23,31,60,84",
++ "auth": "internal-key, oauth-client",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Community",
++ "file": "vendor/openvibe-shared/footer.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "280",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Community",
++ "file": "vendor/openvibe-shared/navbar.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "10,998,1001",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Games",
++ "file": "apps/server/src/net/httpServer.ts",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me, /oauth/token",
++ "lines": "335,367,432,511",
++ "auth": "oauth-client",
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Games",
++ "file": "apps/server/src/net/networkAuth.ts",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "3,18",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "server/seo/seo.js",
++ "target": "OpenVibe.Community",
++ "via": "OV_COMMUNITY_URL",
++ "lines": "424",
++ "auth": "none-detected",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "server/config.js",
++ "target": "OpenVibe.Media",
++ "via": "MEDIA_URL, :4100",
++ "lines": "175,175",
++ "auth": "internal-key",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "server/media-client.js",
++ "target": "OpenVibe.Media",
++ "via": ":4100, MEDIA_URL, /api/v1/${",
++ "lines": "14,21,21,26",
++ "auth": "api-key, bearer",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "server/chat/chat-server.js",
++ "target": "OpenVibe.Network",
++ "via": "OV_NETWORK_INTERNAL_URL",
++ "lines": "84",
++ "auth": "internal-key",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "server/config.js",
++ "target": "OpenVibe.Network",
++ "via": ":4000, OV_NETWORK_URL",
++ "lines": "21,101",
++ "auth": "internal-key",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "server/integrations/platform-oauth.js",
++ "target": "OpenVibe.Network",
++ "via": "/oauth/token",
++ "lines": "52",
++ "auth": "bearer, hmac, oauth-client",
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "server/integrations/powerchat-oauth.js",
++ "target": "OpenVibe.Network",
++ "via": "/oauth/token",
++ "lines": "51",
++ "auth": "bearer, hmac, oauth-client",
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "server/monetization/wallet-client.js",
++ "target": "OpenVibe.Network",
++ "via": "/internal/coins, OV_NETWORK_INTERNAL_URL, :4000",
++ "lines": "9,18,18,71,84,95,143",
++ "auth": "internal-key, bearer",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "server/streaming/golive-notify.js",
++ "target": "OpenVibe.Network",
++ "via": "/internal/events",
++ "lines": "60",
++ "auth": "internal-key",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "server/streaming/restream-manager.js",
++ "target": "OpenVibe.Network",
++ "via": "/oauth/token",
++ "lines": "1387",
++ "auth": "bearer, oauth-client",
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "server/utils/notify.js",
++ "target": "OpenVibe.Network",
++ "via": "OV_NETWORK_INTERNAL_URL, :4000, /internal/notifications",
++ "lines": "16,16,60,114,137",
++ "auth": "internal-key",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "vendor/openvibe-shared/account-switcher.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "176",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "vendor/openvibe-shared/auth-client.js",
++ "target": "OpenVibe.Network",
++ "via": ":4000, /oauth/token",
++ "lines": "23,31,60,84",
++ "auth": "internal-key, oauth-client",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "vendor/openvibe-shared/footer.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "280",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "vendor/openvibe-shared/navbar.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "10,998,1001",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "server/integrations/discord-webhook.js",
++ "target": "webhook",
++ "via": "webhook_url (per-app, configured in DB)",
++ "lines": "16,17,20,21,61",
++ "auth": "none-detected",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Live",
++ "file": "server/media-proxy/webhook.js",
++ "target": "webhook",
++ "via": "webhook_url (per-app, configured in DB)",
++ "lines": "38,52,53",
++ "auth": "hmac",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Media",
++ "file": "server/thumbnails/live-frame-service.js",
++ "target": "OpenVibe.Live",
++ "via": ":3000",
++ "lines": "170",
++ "auth": "none-detected",
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Media",
++ "file": "server/auth.js",
++ "target": "OpenVibe.Network",
++ "via": "network.url, /api/.well-known/jwks",
++ "lines": "28,28,83",
++ "auth": "none-detected",
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Media",
++ "file": "server/user-auth.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/.well-known/jwks, networkUrl, networkInternalUrl, /oauth/token",
++ "lines": "49,57,58,67,67,70,146,227,227,230,361",
++ "auth": "oauth-client",
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Media",
++ "file": "vendor/openvibe-shared/account-switcher.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "176",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Media",
++ "file": "vendor/openvibe-shared/auth-client.js",
++ "target": "OpenVibe.Network",
++ "via": ":4000, /oauth/token",
++ "lines": "23,31,60,84",
++ "auth": "internal-key, oauth-client",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Media",
++ "file": "vendor/openvibe-shared/footer.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "280",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Media",
++ "file": "vendor/openvibe-shared/navbar.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "10,998,1001",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Media",
++ "file": "server/auth.js",
++ "target": "webhook",
++ "via": "webhook_url (per-app, configured in DB)",
++ "lines": "268",
++ "auth": "none-detected",
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Media",
++ "file": "server/webhooks.js",
++ "target": "webhook",
++ "via": "webhook_url (per-app, configured in DB)",
++ "lines": "4,38,48,55",
++ "auth": "hmac",
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/admin/analytics-routes.js",
++ "target": "OpenVibe.Games",
++ "via": ":8000",
++ "lines": "24",
++ "auth": "internal-key, bearer",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/admin/routes.js",
++ "target": "OpenVibe.Games",
++ "via": "games.internalUrl",
++ "lines": "71",
++ "auth": "internal-key",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/index.js",
++ "target": "OpenVibe.Games",
++ "via": ":8000, games.internalUrl",
++ "lines": "219,291",
++ "auth": "internal-key, bearer",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/admin/analytics-routes.js",
++ "target": "OpenVibe.Live",
++ "via": ":3000",
++ "lines": "22",
++ "auth": "internal-key, bearer",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/admin/routes.js",
++ "target": "OpenVibe.Live",
++ "via": "live.internalUrl, OV_LIVE_INTERNAL_URL, :3000",
++ "lines": "69,219,219",
++ "auth": "internal-key",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/chrome/service.js",
++ "target": "OpenVibe.Live",
++ "via": ":3000",
++ "lines": "46,105",
++ "auth": "internal-key",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/index.js",
++ "target": "OpenVibe.Live",
++ "via": ":3000, live.internalUrl",
++ "lines": "215,289,477",
++ "auth": "internal-key, bearer",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/profile/avatar.js",
++ "target": "OpenVibe.Live",
++ "via": "live.internalUrl, :3000",
++ "lines": "64,64",
++ "auth": "internal-key",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/admin/analytics-routes.js",
++ "target": "OpenVibe.Media",
++ "via": ":4100",
++ "lines": "25",
++ "auth": "internal-key, bearer",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/admin/routes.js",
++ "target": "OpenVibe.Media",
++ "via": "media.internalUrl",
++ "lines": "72",
++ "auth": "internal-key",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/index.js",
++ "target": "OpenVibe.Media",
++ "via": ":4100, media.internalUrl",
++ "lines": "218,292",
++ "auth": "internal-key, bearer",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/profile/avatar.js",
++ "target": "OpenVibe.Media",
++ "via": "media.internalUrl, :4100",
++ "lines": "72,72",
++ "auth": "internal-key",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/admin/analytics-routes.js",
++ "target": "OpenVibe.Tools",
++ "via": ":4001",
++ "lines": "23",
++ "auth": "internal-key, bearer",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/admin/routes.js",
++ "target": "OpenVibe.Tools",
++ "via": "tools.internalUrl",
++ "lines": "70",
++ "auth": "internal-key",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/domains/catalog.js",
++ "target": "OpenVibe.Tools",
++ "via": "OV_TOOLS_INTERNAL_URL, :4001",
++ "lines": "17,17",
++ "auth": "none-detected",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Network",
++ "file": "server/index.js",
++ "target": "OpenVibe.Tools",
++ "via": ":4001, tools.internalUrl",
++ "lines": "217,290",
++ "auth": "internal-key, bearer",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Sites",
++ "file": "vendor/openvibe-shared/footer.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "280",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Tools",
++ "file": "apps/gateway/server/index.js",
++ "target": "OpenVibe.Community",
++ "via": "OV_COMMUNITY_URL",
++ "lines": "266",
++ "auth": "internal-key, bearer",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Tools",
++ "file": "apps/gateway/server/index.js",
++ "target": "OpenVibe.Live",
++ "via": "liveUrl",
++ "lines": "148,318",
++ "auth": "internal-key, bearer",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Tools",
++ "file": "apps/audio/server/auth.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/.well-known/jwks, networkUrl, networkInternalUrl",
++ "lines": "9,20,21,40,40",
++ "auth": "none-detected",
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Tools",
++ "file": "apps/docs/server/auth.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/.well-known/jwks, networkUrl, networkInternalUrl",
++ "lines": "9,20,21,40,40",
++ "auth": "none-detected",
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Tools",
++ "file": "apps/gateway/server/auth/routes.js",
++ "target": "OpenVibe.Network",
++ "via": "/oauth/token, /api/.well-known/jwks, networkUrl, networkInternalUrl",
++ "lines": "53,122,130,131,140,140,143,271,271,274,438",
++ "auth": "oauth-client",
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Tools",
++ "file": "apps/gateway/server/index.js",
++ "target": "OpenVibe.Network",
++ "via": "networkUrl",
++ "lines": "317",
++ "auth": "internal-key, bearer",
++ "timeout": true,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Tools",
++ "file": "apps/img/server/auth.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/.well-known/jwks, networkUrl, networkInternalUrl",
++ "lines": "9,20,21,40,40",
++ "auth": "none-detected",
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Tools",
++ "file": "apps/yt/server/auth.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/.well-known/jwks, networkUrl, networkInternalUrl",
++ "lines": "9,20,21,40,40",
++ "auth": "none-detected",
++ "timeout": true,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Tools",
++ "file": "vendor/openvibe-shared/account-switcher.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "176",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": true
++ },
++ {
++ "caller": "OpenVibe.Tools",
++ "file": "vendor/openvibe-shared/auth-client.js",
++ "target": "OpenVibe.Network",
++ "via": ":4000, /oauth/token",
++ "lines": "23,31,60,84",
++ "auth": "internal-key, oauth-client",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Tools",
++ "file": "vendor/openvibe-shared/footer.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "280",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": false
++ },
++ {
++ "caller": "OpenVibe.Tools",
++ "file": "vendor/openvibe-shared/navbar.js",
++ "target": "OpenVibe.Network",
++ "via": "/api/auth/me",
++ "lines": "10,998,1001",
++ "auth": "bearer",
++ "timeout": false,
++ "retry": true
++ }
++ ],
++ "env": [
++ {
++ "repo": "OpenVibe.Community",
++ "name": "BASE_URL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "community.env"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "COOKIE_SECURE",
++ "secret": true,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "HOST",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "community.env"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "LOGIN_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "NODE_ENV",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "community.env"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_AUDIO_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_COMMUNITY_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_DEV_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_DOCS_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_FOOD_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_GAMES_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_IMG_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_LIVE_INTERNAL_URL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "community.env"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_LIVE_URL",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": "community.env"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_LOGO_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_MAPS_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_MEDIA_URL",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": "community.env"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_NET_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_NETWORK_INTERNAL_URL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "community.env"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_NETWORK_LOGIN_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_NETWORK_URL",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": "community.env"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_OAUTH_CLIENT_ID",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "community.env"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_OAUTH_CLIENT_SECRET",
++ "secret": true,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "community.env"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_OAUTH_REDIRECT_URI",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "community.env"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_PASTES_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_TEXT_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_TOOLS_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "OV_YT_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "PORT",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "community.env"
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "name": "TRUST_PROXY",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "community.env"
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "DB_PATH",
++ "secret": false,
++ "files": [
++ "apps/server/src/config.ts"
++ ],
++ "prod": "games.env"
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "EDITOR_KEY",
++ "secret": true,
++ "files": [
++ "apps/server/src/config.ts"
++ ],
++ "prod": "games.env"
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "EVENT_INTERVAL_SCALE",
++ "secret": false,
++ "files": [
++ "apps/server/src/config.ts"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "GUEST_IP_BINDING",
++ "secret": false,
++ "files": [
++ "apps/server/src/config.ts"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "HOST",
++ "secret": false,
++ "files": [
++ "apps/server/src/config.ts"
++ ],
++ "prod": "games.env"
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "LOG_LEVEL",
++ "secret": false,
++ "files": [
++ "apps/server/src/main.ts"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "MAP_PATH",
++ "secret": false,
++ "files": [
++ "apps/server/src/config.ts"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "NODE_ENV",
++ "secret": false,
++ "files": [],
++ "prod": "games.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "OV_NETWORK_AUTH_URL",
++ "secret": false,
++ "files": [
++ "apps/server/src/config.ts"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "OV_NETWORK_URL",
++ "secret": false,
++ "files": [
++ "apps/server/src/config.ts"
++ ],
++ "prod": "games.env"
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "OV_OAUTH_CLIENT_ID",
++ "secret": false,
++ "files": [
++ "apps/server/src/config.ts"
++ ],
++ "prod": "games.env"
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "OV_OAUTH_CLIENT_SECRET",
++ "secret": true,
++ "files": [
++ "apps/server/src/config.ts"
++ ],
++ "prod": "games.env"
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "OWNER_USERNAME",
++ "secret": false,
++ "files": [
++ "apps/server/src/net/networkAuth.ts"
++ ],
++ "prod": "games.env"
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "PLAY_URL",
++ "secret": false,
++ "files": [
++ "apps/server/src/config.ts"
++ ],
++ "prod": "games.env"
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "PORT",
++ "secret": false,
++ "files": [],
++ "prod": "games.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "SELF_URL",
++ "secret": false,
++ "files": [
++ "apps/server/src/config.ts"
++ ],
++ "prod": "games.env"
++ },
++ {
++ "repo": "OpenVibe.Games",
++ "name": "STATIC_DIR",
++ "secret": false,
++ "files": [
++ "apps/server/src/config.ts"
++ ],
++ "prod": "games.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "ADMIN_PASSWORD",
++ "secret": true,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "ADMIN_USERNAME",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "AI_HEAR_MAX_SPOOL",
++ "secret": false,
++ "files": [
++ "server/ai/stream-audio.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "AI_HEAR_SEGMENT_SEC",
++ "secret": false,
++ "files": [
++ "server/ai/stream-audio.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "AI_MOMENTS_PATH",
++ "secret": false,
++ "files": [
++ "server/ai/stream-memory-job.js",
++ "server/arena/arena-service.js",
++ "server/index.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "AI_VOD_MAX_WINDOWS",
++ "secret": false,
++ "files": [
++ "server/ai/media-analysis.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "ALLOW_P2P_FALLBACK",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "ARENA_IMAGE_PATH",
++ "secret": false,
++ "files": [
++ "server/arena/arena-service.js",
++ "server/index.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "BASE_URL",
++ "secret": false,
++ "files": [
++ "server/auth/routes.js",
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "BTTV_CACHE_TTL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "CLIPS_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "COLD_STORAGE_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "DB_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "server/db/database.js",
++ "server/db/init.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "DEBUG_DM_DELIVERY",
++ "secret": false,
++ "files": [
++ "server/chat/chat-server.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "EMOTES_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "server/media-proxy/asset-sync.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "ESCROW_HOLD_DAYS",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "FFZ_CACHE_TTL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "GH_TOKEN",
++ "secret": true,
++ "files": [
++ "server/meta/routes.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "GITHUB_TOKEN",
++ "secret": true,
++ "files": [
++ "server/meta/routes.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "HOST",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "INTERNAL_API_KEY",
++ "secret": true,
++ "files": [
++ "server/chat/chat-server.js",
++ "server/config.js",
++ "server/monetization/wallet-client.js",
++ "server/streaming/routes.js",
++ "server/utils/notify.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "JSMPEG_AUDIO_PORT",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "JSMPEG_VIDEO_PORT",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "JWT_SECRET",
++ "secret": true,
++ "files": [
++ "server/arena/arena-service.js",
++ "server/config.js",
++ "server/integrations/platform-oauth.js",
++ "server/integrations/powerchat-oauth.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "LISTEN_FDS",
++ "secret": false,
++ "files": [
++ "server/index.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "LISTEN_PID",
++ "secret": false,
++ "files": [
++ "server/index.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "LIVE_THUMBS_PATH",
++ "secret": false,
++ "files": [
++ "server/media-proxy/live-thumbs.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "LOGIN_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MAX_EMOTE_SIZE_KB",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MAX_EMOTES_PER_CHANNEL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MAX_EMOTES_PER_UPLOADER_PER_CHANNEL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MAX_EMOTES_PER_USER",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MAX_SOUND_SIZE_KB",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MAX_SOUNDS_PER_CHANNEL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MAX_SOUNDS_PER_UPLOADER_PER_CHANNEL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MAX_VOD_SIZE_MB",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "server/media-proxy/vods.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MEDIA_API_KEY",
++ "secret": true,
++ "files": [
++ "server/media-client.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MEDIA_APP_ID",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "server/media-client.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MEDIA_PUBLIC_URL",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "server/media-client.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MEDIA_URL",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "server/media-client.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MEDIA_WEBHOOK_SECRET",
++ "secret": true,
++ "files": [
++ "server/media-proxy/webhook.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MEDIASOUP_ANNOUNCED_IP",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MEDIASOUP_LISTEN_IP",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MEDIASOUP_MAX_PORT",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MEDIASOUP_MIN_PORT",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MIN_CASHOUT",
++ "secret": false,
++ "files": [],
++ "prod": "live.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "MIN_CASHOUT_BUCKS",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "NODE_ENV",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "server/index.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OFFLINE_SCREEN_PATH",
++ "secret": false,
++ "files": [
++ "server/monetization/routes.js",
++ "server/streaming/routes.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OPS_ALERT_WEBHOOK_URL",
++ "secret": true,
++ "files": [
++ "server/media-proxy/webhook.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_APP_ROOT",
++ "secret": false,
++ "files": [
++ "server/docs/routes.js",
++ "server/web/assets.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_AUDIO_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_COMMUNITY_URL",
++ "secret": false,
++ "files": [
++ "server/index.js",
++ "server/seo/seo.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_DEV_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_DOCS_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_FOOD_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_GAMES_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_IMG_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_INTERNAL_KEY",
++ "secret": true,
++ "files": [
++ "server/chat/chat-server.js",
++ "server/config.js",
++ "server/monetization/wallet-client.js",
++ "server/streaming/routes.js",
++ "server/utils/notify.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_LIVE_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_LOGO_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_MAPS_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_MEDIA_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_NET_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_NETWORK_INTERNAL_URL",
++ "secret": false,
++ "files": [
++ "server/chat/chat-server.js",
++ "server/monetization/wallet-client.js",
++ "server/utils/notify.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_NETWORK_LOGIN_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_NETWORK_PUBLIC_KEY",
++ "secret": false,
++ "files": [
++ "server/auth/auth.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_NETWORK_URL",
++ "secret": false,
++ "files": [
++ "server/auth/auth.js",
++ "server/config.js",
++ "server/index.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_OAUTH_CLIENT_ID",
++ "secret": false,
++ "files": [
++ "server/auth/routes.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_OAUTH_CLIENT_SECRET",
++ "secret": true,
++ "files": [
++ "server/auth/routes.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_PASTES_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_PUBLIC_DIR",
++ "secret": false,
++ "files": [
++ "server/web/assets.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_TEXT_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_TOOLS_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OV_YT_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "OWNER_USERNAME",
++ "secret": false,
++ "files": [
++ "server/db/database.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "PASTES_ON_COMMUNITY",
++ "secret": false,
++ "files": [
++ "server/index.js",
++ "server/seo/seo.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "PATH",
++ "secret": false,
++ "files": [
++ "server/media/media-downloader.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "PAYPAL_CLIENT_ID",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "PAYPAL_CLIENT_SECRET",
++ "secret": true,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "PAYPAL_MODE",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "PORT",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "RS_COMPANION_DB_PATH",
++ "secret": false,
++ "files": [],
++ "prod": "live.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "RS_PASSTHROUGH",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "RS_PASSTHROUGH_INJECT_PARAMS",
++ "secret": false,
++ "files": [
++ "server/integrations/rs-passthrough-relay.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "RS_PASSTHROUGH_ROBOTS",
++ "secret": false,
++ "files": [],
++ "prod": "live.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "RTMP_CHUNK_SIZE",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "RTMP_HOST",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "RTMP_PORT",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "SEVENTV_CACHE_TTL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "SITE_URL",
++ "secret": false,
++ "files": [
++ "server/chat/chat-server.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "SOUND_DEFAULT_MAX_SECONDS",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "SOUNDS_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "server/media-proxy/asset-sync.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "STAR_STREAMER",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "THUMBNAILS_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "TTS_CACHE_PATH",
++ "secret": false,
++ "files": [
++ "server/arena/voice.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "TURN_AUTH_SECRET",
++ "secret": true,
++ "files": [
++ "server/auth/routes.js",
++ "server/net/turn.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "TURN_CREDENTIAL",
++ "secret": true,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "TURN_URL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "TURN_USERNAME",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "UNSAFE_WORDS_PATH",
++ "secret": false,
++ "files": [],
++ "prod": "live.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "VOD_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "WEBRTC_PORT",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "WHIP_PUBLIC_URL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "WHIP_PUBLIC_URL_ENABLED",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "WHISPER_BEAM",
++ "secret": false,
++ "files": [
++ "server/ai/transcribe.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "WHISPER_BIN",
++ "secret": false,
++ "files": [
++ "server/ai/transcribe.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "WHISPER_MAX_CONCURRENT",
++ "secret": false,
++ "files": [
++ "server/ai/transcribe.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "WHISPER_MODEL",
++ "secret": false,
++ "files": [
++ "server/ai/transcribe.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "WHISPER_MODEL_LIVE",
++ "secret": false,
++ "files": [
++ "server/ai/transcribe.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "WHISPER_MODEL_MULTI",
++ "secret": false,
++ "files": [
++ "server/ai/transcribe.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "WHISPER_THREADS",
++ "secret": false,
++ "files": [
++ "server/ai/transcribe.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "WHISPER_VAD",
++ "secret": false,
++ "files": [
++ "server/ai/transcribe.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "WHISPER_VAD_MODEL",
++ "secret": false,
++ "files": [
++ "server/ai/transcribe.js"
++ ],
++ "prod": "live.env"
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "YAMNET_CLASSMAP",
++ "secret": false,
++ "files": [
++ "server/ai/audio-events.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "YAMNET_MIN_CONFIDENCE",
++ "secret": false,
++ "files": [
++ "server/ai/audio-events.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "YAMNET_MODEL",
++ "secret": false,
++ "files": [
++ "server/ai/audio-events.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "YT_DAILY_QUOTA",
++ "secret": false,
++ "files": [
++ "server/integrations/platform-oauth.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "YT_MIN_CREATE_INTERVAL_MS",
++ "secret": false,
++ "files": [
++ "server/integrations/platform-oauth.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "YT_QUOTA_COOLDOWN_MS",
++ "secret": false,
++ "files": [
++ "server/integrations/platform-oauth.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "YT_QUOTA_HARD",
++ "secret": false,
++ "files": [
++ "server/integrations/platform-oauth.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "YT_QUOTA_SOFT",
++ "secret": false,
++ "files": [
++ "server/integrations/platform-oauth.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "YTDLP_NODE_PATH",
++ "secret": false,
++ "files": [
++ "server/media/media-downloader.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "name": "YTDLP_PATH",
++ "secret": false,
++ "files": [
++ "server/media/media-downloader.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "APP_INTERNAL_URLS",
++ "secret": false,
++ "files": [
++ "server/thumbnails/live-frame-service.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "ASSETS_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "CLIP_MAX_CONCURRENT_FFMPEG",
++ "secret": false,
++ "files": [
++ "server/vod/clip-cutter.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "CLIP_QUEUE_WAIT_MS",
++ "secret": false,
++ "files": [
++ "server/vod/clip-cutter.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "CLIPS_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "DB_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "FILES_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "HOST",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "INTERNAL_API_KEY",
++ "secret": true,
++ "files": [
++ "server/config.js",
++ "server/views/service.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "LIVE_APP_INTERNAL_URL",
++ "secret": false,
++ "files": [
++ "server/thumbnails/live-frame-service.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "LOGIN_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_APP_KEYS",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_APPS_SEED",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_B2_APP_KEY",
++ "secret": true,
++ "files": [
++ "server/vod/vod-storage.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_B2_BUCKET",
++ "secret": false,
++ "files": [
++ "server/vod/vod-storage.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_B2_ENDPOINT",
++ "secret": false,
++ "files": [
++ "server/vod/vod-storage.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_B2_KEY_ID",
++ "secret": false,
++ "files": [
++ "server/vod/vod-storage.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_B2_REGION",
++ "secret": false,
++ "files": [
++ "server/vod/vod-storage.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_PUBLIC_URL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_R2_ACCESS_KEY_ID",
++ "secret": false,
++ "files": [
++ "server/vod/vod-storage.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_R2_BUCKET",
++ "secret": false,
++ "files": [
++ "server/vod/vod-storage.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_R2_ENDPOINT",
++ "secret": false,
++ "files": [
++ "server/vod/vod-storage.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_R2_REGION",
++ "secret": false,
++ "files": [
++ "server/vod/vod-storage.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_R2_SECRET_ACCESS_KEY",
++ "secret": true,
++ "files": [
++ "server/vod/vod-storage.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MEDIA_SECRET",
++ "secret": true,
++ "files": [
++ "server/views/service.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "MIN_VOD_SECONDS",
++ "secret": false,
++ "files": [
++ "server/vod/finalize.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "NODE_ENV",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_AUDIO_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_COMMUNITY_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_DEV_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_DOCS_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_FOOD_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_GAMES_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_IMG_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_LIVE_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_LOGO_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_MAPS_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_MEDIA_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_NET_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_NETWORK_INTERNAL_URL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_NETWORK_LOGIN_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_NETWORK_URL",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_OAUTH_CLIENT_ID",
++ "secret": false,
++ "files": [
++ "server/index.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_OAUTH_CLIENT_SECRET",
++ "secret": true,
++ "files": [
++ "server/index.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_OAUTH_REDIRECT_URI",
++ "secret": false,
++ "files": [
++ "server/index.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_PASTES_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_TEXT_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_TOOLS_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "OV_YT_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "PASTES_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "PORT",
++ "secret": false,
++ "files": [],
++ "prod": "media.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "RTP_PORT_MAX",
++ "secret": false,
++ "files": [],
++ "prod": "media.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "RTP_PORT_MIN",
++ "secret": false,
++ "files": [],
++ "prod": "media.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "THUMBNAILS_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "VIEW_HASH_SECRET",
++ "secret": true,
++ "files": [
++ "server/views/service.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "VOD_DISK_CRIT_GB",
++ "secret": false,
++ "files": [
++ "server/vod/recorder.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "VOD_DISK_WARN_GB",
++ "secret": false,
++ "files": [
++ "server/vod/recorder.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "VOD_NO_NORMALIZE",
++ "secret": false,
++ "files": [
++ "server/vod/recorder.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "VOD_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "media.env"
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "name": "VOD_STOP_GRACE_MS",
++ "secret": false,
++ "files": [
++ "server/vod/recorder.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "ADMIN_PASSWORD",
++ "secret": true,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "ADMIN_USERNAME",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "AVATAR_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "BASE_URL",
++ "secret": false,
++ "files": [
++ "server/admin/routes.js",
++ "server/config.js",
++ "server/index.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "BOOTSTRAP_PROFILE",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "server/db/database.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "CERTBOT_BIN",
++ "secret": false,
++ "files": [
++ "server/deploy/cert-manager.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "DB_PATH",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "server/grant-admin.js",
++ "server/reset-db.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "HOST",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "INTERNAL_API_KEY",
++ "secret": true,
++ "files": [
++ "server/admin/routes.js",
++ "server/config.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "INTERNAL_URL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "JWT_PRIVATE_KEY",
++ "secret": true,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "JWT_PUBLIC_KEY",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "LETSENCRYPT_DIR",
++ "secret": false,
++ "files": [
++ "server/deploy/cert-manager.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "LOGIN_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js",
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "MEDIASOUP_ANNOUNCED_IP",
++ "secret": false,
++ "files": [
++ "server/domains/dns-check.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "NODE_ENV",
++ "secret": false,
++ "files": [
++ "server/auth/fedcm.js",
++ "server/auth/sso-check.js",
++ "server/config.js",
++ "server/db/database.js",
++ "server/index.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "NOTIFICATION_EMAIL_CRITICAL_ONLY",
++ "secret": false,
++ "files": [],
++ "prod": "network.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "NOTIFICATION_MAX_AGE_DAYS",
++ "secret": false,
++ "files": [],
++ "prod": "network.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "NOTIFICATIONS_ENABLED",
++ "secret": false,
++ "files": [],
++ "prod": "network.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OPENVIBELIVE_DB_PATH",
++ "secret": false,
++ "files": [
++ "server/db/database.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_AUDIO_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_COMMUNITY_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_DEV_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_DOCS_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_FOOD_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_GAMES_INTERNAL_URL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_GAMES_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_GAMES_WEBHOOK_SECRET",
++ "secret": true,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_IMG_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_INTERNAL_KEY",
++ "secret": true,
++ "files": [
++ "server/admin/routes.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_LIVE_INTERNAL_URL",
++ "secret": false,
++ "files": [
++ "server/admin/routes.js",
++ "server/config.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_LIVE_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_LIVE_WEBHOOK_SECRET",
++ "secret": true,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_LOGO_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_MAPS_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_MEDIA_INTERNAL_URL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_MEDIA_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_NET_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_NETWORK_BASE_URL",
++ "secret": false,
++ "files": [
++ "server/auth/reset-tokens.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_NETWORK_INTERNAL_URL",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_NETWORK_LOGIN_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_NETWORK_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js",
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_PASTES_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_SSO_TARGETS",
++ "secret": false,
++ "files": [
++ "server/auth/sso-targets.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_SSO_TARGETS_DISABLED",
++ "secret": false,
++ "files": [
++ "server/auth/sso-targets.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_TEXT_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_TOOLS_INTERNAL_URL",
++ "secret": false,
++ "files": [
++ "server/config.js",
++ "server/domains/catalog.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_TOOLS_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OV_YT_URL",
++ "secret": false,
++ "files": [
++ "packages/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "OWNER_USERNAME",
++ "secret": false,
++ "files": [
++ "server/admin/routes.js",
++ "server/auth/owner-guard.js",
++ "server/auth/routes.js",
++ "server/deploy/routes.js",
++ "server/index.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "PORT",
++ "secret": false,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": "network.env"
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "PUBLIC_IP",
++ "secret": false,
++ "files": [
++ "server/domains/dns-check.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "SETUP_TOKEN",
++ "secret": true,
++ "files": [
++ "server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "SSH_PROJECTS_ROOT",
++ "secret": false,
++ "files": [
++ "server/index.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "name": "SSH_SERVER_HOST",
++ "secret": false,
++ "files": [
++ "server/index.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "BASE_URL",
++ "secret": false,
++ "files": [
++ "apps/gateway/server/config.js",
++ "apps/maps/server/config.js",
++ "apps/text/server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "COOKIE_DOMAIN",
++ "secret": true,
++ "files": [
++ "apps/gateway/server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "COOKIE_SECURE",
++ "secret": true,
++ "files": [
++ "apps/gateway/server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "DATA_DIR",
++ "secret": false,
++ "files": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/img/server/config.js",
++ "apps/yt/server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "DOWNLOADS_DIR",
++ "secret": false,
++ "files": [
++ "apps/yt/server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "HOST",
++ "secret": false,
++ "files": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/gateway/server/config.js",
++ "apps/img/server/config.js",
++ "apps/maps/server/config.js",
++ "apps/text/server/config.js",
++ "apps/yt/server/config.js"
++ ],
++ "prod": "tools.env"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "INTERNAL_API_KEY",
++ "secret": true,
++ "files": [
++ "apps/_shared/internal-auth.js"
++ ],
++ "prod": "tools.env"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "LIVE_URL",
++ "secret": false,
++ "files": [
++ "apps/gateway/server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "LOGIN_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "MAPS_API",
++ "secret": false,
++ "files": [
++ "apps/food/server/index.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "MEDIA_PUBLIC_URL",
++ "secret": false,
++ "files": [],
++ "prod": "tools.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "MEDIA_URL",
++ "secret": false,
++ "files": [],
++ "prod": "tools.env",
++ "unreferenced": true
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "NODE_ENV",
++ "secret": false,
++ "files": [
++ "apps/audio/server/index.js",
++ "apps/docs/server/index.js",
++ "apps/food/server/index.js",
++ "apps/gateway/server/config.js",
++ "apps/img/server/index.js",
++ "apps/text/server/index.js",
++ "apps/yt/server/index.js",
++ "apps/yt/server/seo.js"
++ ],
++ "prod": "tools.env"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "NPS_API_KEY",
++ "secret": true,
++ "files": [
++ "apps/maps/server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OPEN_CHARGE_MAP_KEY",
++ "secret": true,
++ "files": [
++ "apps/maps/server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OUTPUT_DIR",
++ "secret": false,
++ "files": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/img/server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_AUDIO_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_COMMUNITY_URL",
++ "secret": false,
++ "files": [
++ "apps/gateway/server/index.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_DEV_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_DOCS_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_DOMAINS_URL",
++ "secret": false,
++ "files": [
++ "apps/gateway/server/registry/index.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_ENFORCE_HOSTS",
++ "secret": false,
++ "files": [
++ "apps/gateway/server/index.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_EXTRA_HOSTS",
++ "secret": false,
++ "files": [
++ "apps/_shared/host-role.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_FOOD_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_GAMES_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_IMG_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_INTERNAL_KEY",
++ "secret": true,
++ "files": [
++ "apps/_shared/internal-auth.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_LIVE_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_LOGO_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_MAPS_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_MEDIA_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_NET_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_NETWORK_INTERNAL_URL",
++ "secret": false,
++ "files": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/gateway/server/config.js",
++ "apps/img/server/config.js",
++ "apps/yt/server/config.js"
++ ],
++ "prod": "tools.env"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_NETWORK_LOGIN_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_NETWORK_PUBLIC_KEY",
++ "secret": false,
++ "files": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/img/server/config.js",
++ "apps/yt/server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_NETWORK_URL",
++ "secret": false,
++ "files": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/gateway/server/config.js",
++ "apps/img/server/config.js",
++ "apps/text/server/config.js",
++ "apps/yt/server/config.js",
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": "tools.env"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_OAUTH_CLIENT_ID",
++ "secret": false,
++ "files": [
++ "apps/gateway/server/config.js"
++ ],
++ "prod": "tools.env"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_OAUTH_CLIENT_SECRET",
++ "secret": true,
++ "files": [
++ "apps/gateway/server/config.js"
++ ],
++ "prod": "tools.env"
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_OAUTH_REDIRECT_URI",
++ "secret": false,
++ "files": [
++ "apps/gateway/server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_PASTES_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_TEXT_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_TOOLS_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "OV_YT_URL",
++ "secret": false,
++ "files": [
++ "vendor/openvibe-shared/brand.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "PORT",
++ "secret": false,
++ "files": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/food/server/index.js",
++ "apps/gateway/server/config.js",
++ "apps/img/server/config.js",
++ "apps/maps/server/config.js",
++ "apps/text/server/config.js",
++ "apps/yt/server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "RIDB_API_KEY",
++ "secret": true,
++ "files": [
++ "apps/maps/server/config.js",
++ "apps/maps/server/sources/ridb.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "UPLOADS_DIR",
++ "secret": false,
++ "files": [
++ "apps/audio/server/config.js",
++ "apps/docs/server/config.js",
++ "apps/img/server/config.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "YT_COOKIES_FILE",
++ "secret": false,
++ "files": [
++ "apps/yt/server/downloader.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "YT_PROXY",
++ "secret": false,
++ "files": [
++ "apps/yt/server/downloader.js"
++ ],
++ "prod": null
++ },
++ {
++ "repo": "OpenVibe.Tools",
++ "name": "YTDLP_PATH",
++ "secret": false,
++ "files": [
++ "apps/yt/server/config.js"
++ ],
++ "prod": null
++ }
++ ],
++ "discrepancies": [
++ {
++ "severity": "low",
++ "area": "database",
++ "subject": "/opt/openvibe.live/data/backups/live-pre-643a2e6.db",
++ "detail": "264.7 MB backup inside the service data dir"
++ },
++ {
++ "severity": "low",
++ "area": "database",
++ "subject": "/opt/openvibe.live/data/hobo.db",
++ "detail": "0 B, zero tables (dead file)"
++ },
++ {
++ "severity": "low",
++ "area": "database",
++ "subject": "/opt/openvibe.live/data/openvibe.db",
++ "detail": "0 B, zero tables (dead file)"
++ },
++ {
++ "severity": "low",
++ "area": "deploy",
++ "subject": "games",
++ "detail": "deployed 43c11f0 has the same file tree as origin/main 5710287 but a different SHA (pre-rewrite history); reset the checkout so later pulls fast-forward"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Community:COOKIE_SECURE",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Live:GH_TOKEN",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Live:GITHUB_TOKEN",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Live:OPS_ALERT_WEBHOOK_URL",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Live:OV_INTERNAL_KEY",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Media:MEDIA_SECRET",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Media:VIEW_HASH_SECRET",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Network:OV_GAMES_WEBHOOK_SECRET",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Network:OV_INTERNAL_KEY",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Network:OV_LIVE_WEBHOOK_SECRET",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Network:SETUP_TOKEN",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Tools:COOKIE_DOMAIN",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Tools:COOKIE_SECURE",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Tools:NPS_API_KEY",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Tools:OPEN_CHARGE_MAP_KEY",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Tools:OV_INTERNAL_KEY",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "env",
++ "subject": "OpenVibe.Tools:RIDB_API_KEY",
++ "detail": "secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:active_effects",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:ai_conversations",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:ai_memory",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:analytics_snapshots",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_achievements",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_battles",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_events",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_talk",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_talk_hype",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_talk_session_hype",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_talk_session_topics",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_talk_sessions",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_talk_topics",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_tier_paid",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_topic_hype",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_topic_members",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_topic_moments",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_topic_progress",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_topic_sides",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_topic_threads",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_topics",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_viewer_clout",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:arena_votes",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:bank_items",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:battle_stats",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:canvas_actions",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:canvas_bans",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:canvas_region_locks",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:canvas_settings",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:canvas_snapshots",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:canvas_tiles",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:canvas_user_overrides",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:changelog_entries",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:chat_log",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:chatter_profiles",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:chatter_subjects",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:chatter_xp_log",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:custom_rewards",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:daily_claims",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:divorce_history",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:dungeon_stats",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:event_log",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:farm_plots",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:file_checksums",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:fish_profile",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:gusher_profile",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:inventory",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:jo_profile",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:love_affinity",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:love_profile",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:love_proposals",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:mine_profile",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:mod_persuasion",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:music_profile",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:promo_claims",
++ "detail": "in production (live.db) but no CREATE TABLE in the repo; classified investigate -> archive then drop"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:roast_dossier",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:roast_log",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:unlocked_recipes",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:user_voice_selection",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:user_voices",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "low",
++ "area": "schema",
++ "subject": "OpenVibe.Live:woodcut_profile",
++ "detail": "in production (rs-companion.db) but no CREATE TABLE in the repo; classified frozen-legacy -> OpenVibe.Games (import source)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Games:NODE_ENV",
++ "detail": "set in games.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Games:PORT",
++ "detail": "set in games.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Live:MIN_CASHOUT",
++ "detail": "set in live.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Live:RS_COMPANION_DB_PATH",
++ "detail": "set in live.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Live:RS_PASSTHROUGH_ROBOTS",
++ "detail": "set in live.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Live:UNSAFE_WORDS_PATH",
++ "detail": "set in live.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Media:PORT",
++ "detail": "set in media.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Media:RTP_PORT_MAX",
++ "detail": "set in media.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Media:RTP_PORT_MIN",
++ "detail": "set in media.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Network:NOTIFICATION_EMAIL_CRITICAL_ONLY",
++ "detail": "set in network.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Network:NOTIFICATION_MAX_AGE_DAYS",
++ "detail": "set in network.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Network:NOTIFICATIONS_ENABLED",
++ "detail": "set in network.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Tools:MEDIA_PUBLIC_URL",
++ "detail": "set in tools.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "env",
++ "subject": "OpenVibe.Tools:MEDIA_URL",
++ "detail": "set in tools.env but not referenced by scanned code (may be read by a dependency or stale)"
++ },
++ {
++ "severity": "info",
++ "area": "schema",
++ "subject": "OpenVibe.Live:channel_sounds_new",
++ "detail": "declared in code but absent from every production database for this service (lazy/unused/legacy migration)"
++ },
++ {
++ "severity": "info",
++ "area": "schema",
++ "subject": "OpenVibe.Live:chat_messages_new",
++ "detail": "declared in code but absent from every production database for this service (lazy/unused/legacy migration)"
++ },
++ {
++ "severity": "info",
++ "area": "schema",
++ "subject": "OpenVibe.Live:emotes_new",
++ "detail": "declared in code but absent from every production database for this service (lazy/unused/legacy migration)"
++ },
++ {
++ "severity": "info",
++ "area": "schema",
++ "subject": "OpenVibe.Live:robotstreamer_integrations_new",
++ "detail": "declared in code but absent from every production database for this service (lazy/unused/legacy migration)"
++ },
++ {
++ "severity": "info",
++ "area": "schema",
++ "subject": "OpenVibe.Live:stream_controls_new",
++ "detail": "declared in code but absent from every production database for this service (lazy/unused/legacy migration)"
++ },
++ {
++ "severity": "info",
++ "area": "schema",
++ "subject": "OpenVibe.Media:analytics_daily",
++ "detail": "declared in code but absent from every production database for this service (lazy/unused/legacy migration)"
++ },
++ {
++ "severity": "info",
++ "area": "schema",
++ "subject": "OpenVibe.Media:analytics_events",
++ "detail": "declared in code but absent from every production database for this service (lazy/unused/legacy migration)"
++ },
++ {
++ "severity": "info",
++ "area": "schema",
++ "subject": "OpenVibe.Media:analytics_hourly",
++ "detail": "declared in code but absent from every production database for this service (lazy/unused/legacy migration)"
++ },
++ {
++ "severity": "info",
++ "area": "schema",
++ "subject": "OpenVibe.Media:analytics_rate_tracking",
++ "detail": "declared in code but absent from every production database for this service (lazy/unused/legacy migration)"
++ },
++ {
++ "severity": "info",
++ "area": "schema",
++ "subject": "OpenVibe.Network:admin_rate_limits",
++ "detail": "declared in code but absent from every production database for this service (lazy/unused/legacy migration)"
++ },
++ {
++ "severity": "unknown",
++ "area": "host",
++ "subject": "powerchat.gg, raspi/hobo.tools (LAN)",
++ "detail": "unreachable from this environment; state not verified"
++ },
++ {
++ "severity": "unknown",
++ "area": "provider",
++ "subject": "B2/R2/Cloudflare/PayPal consoles",
++ "detail": "console configuration (billing, lifecycle rules, zone settings, app config) is not visible from this environment"
++ }
++ ],
++ "metrics": {
++ "github.charters": "21 charter-only repos of 32",
++ "contract.todo-markers": "Live 15, Network 0, Media 0, Tools 0, Community 0, Games 0, Sites 0",
++ "outbound.call-sites": "65 caller-file/target pairs across 50 files",
++ "outbound.internal-key": "28 of 65 call sites authenticate with the shared internal key",
++ "ws.servers": "10 WebSocket server constructions",
++ "tables.analytics-copies": "5 repos carry their own analytics_events",
++ "vendored.copies": "5 repos vendor openvibe-shared (129 files)",
++ "vendored.divergent": "4 divergent: Community/ov-icons.js, Live/user-card.js, Media/ov-icons.js, Tools/ov-icons.js",
++ "ci.repos": "0 of 7 scanned repos have .github/workflows",
++ "deploy.drift": "1 service(s) not running origin/main",
++ "tests.OpenVibe.Live": "55 test files",
++ "tables.OpenVibe.Live": "125 tables",
++ "tables.OpenVibe.Live.target:OpenVibe.Games (import source)": "31 Live tables target OpenVibe.Games (import source)",
++ "tables.OpenVibe.Live.target:OpenVibe.AI": "14 Live tables target OpenVibe.AI",
++ "tables.OpenVibe.Live.target:OpenVibe.Live": "26 Live tables target OpenVibe.Live",
++ "tables.OpenVibe.Live.target:OpenVibe.Network (policy)": "5 Live tables target OpenVibe.Network (policy)",
++ "tables.OpenVibe.Live.target:OpenVibe.Network (principals) / OpenVibe.Codes": "2 Live tables target OpenVibe.Network (principals) / OpenVibe.Codes",
++ "tables.OpenVibe.Live.target:archive then drop": "23 Live tables target archive then drop",
++ "tables.OpenVibe.Live.target:OpenRe.Stream": "14 Live tables target OpenRe.Stream",
++ "tables.OpenVibe.Live.target:OpenVibe.Games": "22 Live tables target OpenVibe.Games",
++ "tables.OpenVibe.Live.target:OpenVibe.Chat": "23 Live tables target OpenVibe.Chat",
++ "tables.OpenVibe.Live.target:OpenVibe.Billing (loyalty ledger)": "7 Live tables target OpenVibe.Billing (loyalty ledger)",
++ "tables.OpenVibe.Live.target:OpenVibe.Media": "2 Live tables target OpenVibe.Media",
++ "tables.OpenVibe.Live.target:OpenVibe.Community": "4 Live tables target OpenVibe.Community",
++ "tables.OpenVibe.Live.target:OpenVibe.Tips": "1 Live tables target OpenVibe.Tips",
++ "tables.OpenVibe.Live.target:OpenVibe.Network": "7 Live tables target OpenVibe.Network",
++ "tables.OpenVibe.Live.target:OpenVibe.Billing": "2 Live tables target OpenVibe.Billing",
++ "tables.OpenVibe.Live.target:OpenVibe.Billing + OpenVibe.VIP": "1 Live tables target OpenVibe.Billing + OpenVibe.VIP",
++ "tables.OpenVibe.Live.target:OpenVibe.Codes": "2 Live tables target OpenVibe.Codes",
++ "tests.OpenVibe.Network": "17 test files",
++ "tables.OpenVibe.Network": "35 tables",
++ "tables.OpenVibe.Network.target:OpenVibe.Network": "32 Network tables target OpenVibe.Network",
++ "tables.OpenVibe.Network.target:OpenVibe.Billing (loyalty ledger)": "2 Network tables target OpenVibe.Billing (loyalty ledger)",
++ "tables.OpenVibe.Network.target:OpenVibe.Events": "1 Network tables target OpenVibe.Events",
++ "tests.OpenVibe.Media": "4 test files",
++ "tables.OpenVibe.Media": "15 tables",
++ "tables.OpenVibe.Media.target:OpenVibe.Media": "12 Media tables target OpenVibe.Media",
++ "tables.OpenVibe.Media.target:OpenVibe.Community": "3 Media tables target OpenVibe.Community",
++ "tests.OpenVibe.Tools": "2 test files",
++ "tables.OpenVibe.Tools": "4 tables",
++ "tables.OpenVibe.Tools.target:OpenVibe.Tools": "4 Tools tables target OpenVibe.Tools",
++ "tests.OpenVibe.Community": "8 test files",
++ "tables.OpenVibe.Community": "4 tables",
++ "tables.OpenVibe.Community.target:OpenVibe.Community": "4 Community tables target OpenVibe.Community",
++ "tests.OpenVibe.Games": "56 test files",
++ "tables.OpenVibe.Games": "5 tables",
++ "tables.OpenVibe.Games.target:OpenVibe.Games": "5 Games tables target OpenVibe.Games",
++ "tests.OpenVibe.Sites": "0 test files",
++ "tables.OpenVibe.Sites": "0 tables"
++ },
++ "dStatus": [
++ {
++ "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",
++ "ok": true
++ },
++ {
++ "repo": "OpenVibe.Community",
++ "path": "server/live-client.js",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "tables.OpenVibe.Live.target:OpenVibe.Network",
++ "value": "7 Live tables target OpenVibe.Network"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ },
++ {
++ "repo": "OpenVibe.Sites",
++ "path": "sites.json",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "github.charters",
++ "value": "21 charter-only repos of 32"
++ },
++ {
++ "key": "contract.todo-markers",
++ "value": "Live 15, Network 0, Media 0, Tools 0, Community 0, Games 0, Sites 0"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "path": "server/auth/permissions.js",
++ "ok": true
++ }
++ ],
++ "metrics": [],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "path": "server/notifications",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "tables.analytics-copies",
++ "value": "5 repos carry their own analytics_events"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "outbound.call-sites",
++ "value": "65 caller-file/target pairs across 50 files"
++ },
++ {
++ "key": "ws.servers",
++ "value": "10 WebSocket server constructions"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ },
++ {
++ "repo": "OpenVibe.Media",
++ "path": "server/pastes",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "tables.OpenVibe.Media.target:OpenVibe.Community",
++ "value": "3 Media tables target OpenVibe.Community"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "path": "server/streaming/restream-manager.js",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "tables.OpenVibe.Live.target:OpenRe.Stream",
++ "value": "14 Live tables target OpenRe.Stream"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "path": "server/streaming/call-server.js",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "tables.OpenVibe.Live.target:OpenVibe.Chat",
++ "value": "23 Live tables target OpenVibe.Chat"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "tables.OpenVibe.Community",
++ "value": "4 tables"
++ },
++ {
++ "key": "tables.OpenVibe.Live.target:OpenVibe.Community",
++ "value": "4 Live tables target OpenVibe.Community"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ },
++ {
++ "repo": "OpenVibe.Network",
++ "path": "server/coins",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "tables.OpenVibe.Live.target:OpenVibe.Billing",
++ "value": "2 Live tables target OpenVibe.Billing"
++ },
++ {
++ "key": "tables.OpenVibe.Network.target:OpenVibe.Billing",
++ "value": "2 Network tables target OpenVibe.Billing (loyalty ledger)"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "tests.OpenVibe.Tools",
++ "value": "2 test files"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "tests.OpenVibe.Games",
++ "value": "56 test files"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "ids": "D33-D34",
++ "name": "Codes, Host",
++ "status": "absent",
++ "disposition": "missing",
++ "wave": "W20, W21",
++ "finding": "Charter repositories only.",
++ "evidence": [],
++ "metrics": [
++ {
++ "key": "github.charters",
++ "value": "21 charter-only repos of 32"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "path": "server/ai/llm.js",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "tables.OpenVibe.Live.target:OpenVibe.AI",
++ "value": "14 Live tables target OpenVibe.AI"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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": [],
++ "metrics": [],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "outbound.internal-key",
++ "value": "28 of 65 call sites authenticate with the shared internal key"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ }
++ ],
++ "hazards": [
++ "H1",
++ "H3"
++ ],
++ "metrics": [],
++ "verified": true
++ },
++ {
++ "ids": "D42",
++ "name": "Shared component architecture",
++ "status": "partial",
++ "disposition": "partial",
++ "wave": "W2, Track R",
++ "finding": "openvibe-shared is vendored by copy; consumers can drift from canonical.",
++ "evidence": [
++ {
++ "repo": "OpenVibe.Network",
++ "path": "packages/openvibe-shared",
++ "ok": true
++ }
++ ],
++ "metrics": [
++ {
++ "key": "vendored.copies",
++ "value": "5 repos vendor openvibe-shared (129 files)"
++ },
++ {
++ "key": "vendored.divergent",
++ "value": "4 divergent: Community/ov-icons.js, Live/user-card.js, Media/ov-icons.js, Tools/ov-icons.js"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ },
++ {
++ "repo": "OpenVibe.Live",
++ "path": "server/web/assets.js",
++ "ok": true
++ }
++ ],
++ "metrics": [],
++ "verified": true
++ },
++ {
++ "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",
++ "ok": true
++ },
++ {
++ "repo": "OpenVibe.Sites",
++ "path": "build.js",
++ "ok": true
++ }
++ ],
++ "metrics": [],
++ "verified": true
++ },
++ {
++ "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": [
++ {
++ "key": "deploy.drift",
++ "value": "1 service(s) not running origin/main"
++ }
++ ],
++ "verified": true
++ },
++ {
++ "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": [
++ {
++ "key": "ci.repos",
++ "value": "0 of 7 scanned repos have .github/workflows"
++ }
++ ],
++ "verified": true
++ }
++ ],
++ "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",
++ "checkResult": null
++ },
++ {
++ "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",
++ "checkResult": "openvibe-live.socket is active/running"
++ },
++ {
++ "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",
++ "checkResult": null
++ },
++ {
++ "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",
++ "checkResult": null
++ },
++ {
++ "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",
++ "checkResult": "WHISPER_MODEL_MULTI is set in live.env"
++ },
++ {
++ "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",
++ "checkResult": "YT_PROXY still unset in tools.env"
++ },
++ {
++ "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",
++ "checkResult": "workstation / is 82% used (at generation time)"
++ },
++ {
++ "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",
++ "checkResult": null
++ },
++ {
++ "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",
++ "checkResult": null
++ },
++ {
++ "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",
++ "checkResult": "main history violates the rule in: Live (238 commits with assistant trailers); Network (14 commits with assistant trailers); Media (30 commits with assistant trailers); Tools (9 commits with assistant trailers)"
++ },
++ {
++ "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": "Network maintainers (canonical 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",
++ "checkResult": "live/data/analytics.db 1.75 GB; network/data/network.db 766.5 MB; tools/apps/audio/data/analytics.db 59.9 MB"
++ },
++ {
++ "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",
++ "checkResult": null
++ },
++ {
++ "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",
++ "checkResult": "games: deployed 43c11f0 has the same file tree as origin/main 5710287 but a different SHA (pre-rewrite history); reset the checkout so later pulls fast-forward"
++ },
++ {
++ "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",
++ "checkResult": "live/data/backups/live-pre-643a2e6.db, live/data/hobo.db, live/data/openvibe.db"
++ }
++ ],
++ "criteria": [
++ {
++ "id": "EC1",
++ "text": "Every table has an owner classification",
++ "pass": true,
++ "detail": "249 tables classified, 0 unclassified"
++ },
++ {
++ "id": "EC2",
++ "text": "Every route is attributed to a repo and source line",
++ "pass": true,
++ "detail": "926 routes; 0 router mounts whose module could not be resolved (their routes are listed without the mount prefix)"
++ },
++ {
++ "id": "EC3",
++ "text": "Every background job is listed with its location",
++ "pass": true,
++ "detail": "82 timers/jobs"
++ },
++ {
++ "id": "EC4",
++ "text": "Every cross-service call records caller, callee, auth, timeout and retry",
++ "pass": true,
++ "detail": "65 call sites; 30 with no timeout detected"
++ },
++ {
++ "id": "EC5",
++ "text": "Unknowns are marked unknown, not guessed",
++ "pass": true,
++ "detail": "2 unknown items recorded"
++ },
++ {
++ "id": "EC6",
++ "text": "D01-D46 status resolved from evidence",
++ "pass": true,
++ "detail": "22/22 families verified"
++ },
++ {
++ "id": "EC7",
++ "text": "Hazard register reviewed by the production-host owner",
++ "pass": false,
++ "detail": "pending: set reviewedBy in data/hazards.json after review"
++ }
++ ]
++}
ddiff --git a/package.json b/package.json
iindex c0d13df8..bb68de38 100644
---- a/package.json
++++ b/package.json
@@@ -6,7 +6,7 @@
"scripts": {
"start": "node server/index.js",
"dev": "NODE_ENV=development node server/index.js",
-- "test": "node test/registry-bootstrap.test.js && node packages/openvibe-shared/test/theme-parity.test.js && node test/nginx-generator-live.test.js && node test/admin-refresh.test.js && node packages/openvibe-shared/test/url-resolver.test.js && node packages/openvibe-shared/test/url-resolver-deploy.test.js && node packages/openvibe-shared/test/navbar-auth.test.js && node packages/openvibe-shared/test/navbar-brand.test.js && node test/history-sso.test.js && node test/sso-check.test.js && node test/fedcm.test.js && node test/domains.test.js && node packages/openvibe-shared/test/shared-modules.test.js && node test/chrome.test.js && node packages/openvibe-shared/test/panels.test.js && node test/avatar.test.js",
++ "test": "node test/registry-bootstrap.test.js && node packages/openvibe-shared/test/theme-parity.test.js && node test/nginx-generator-live.test.js && node test/admin-refresh.test.js && node packages/openvibe-shared/test/url-resolver.test.js && node packages/openvibe-shared/test/url-resolver-deploy.test.js && node packages/openvibe-shared/test/navbar-auth.test.js && node packages/openvibe-shared/test/navbar-brand.test.js && node test/history-sso.test.js && node test/sso-check.test.js && node test/fedcm.test.js && node test/domains.test.js && node packages/openvibe-shared/test/shared-modules.test.js && node test/chrome.test.js && node packages/openvibe-shared/test/panels.test.js && node test/avatar.test.js && node test/roadmap-baseline.test.js",
"reset-db": "node server/reset-db.js",
"grant-admin": "node server/grant-admin.js"
},
ddiff --git a/scripts/roadmap-baseline/collect-github.sh b/scripts/roadmap-baseline/collect-github.sh
nnew file mode 100755
iindex 00000000..d5e44009
---- /dev/null
++++ b/scripts/roadmap-baseline/collect-github.sh
@@@ -0,0 +1,27 @@
++#!/usr/bin/env bash
++# Snapshot of the OpenVibers GitHub account for the Wave 0 baseline (read-only, via gh).
++# Writes docs/roadmap-baseline/data/github.json: every repository plus, for charter-only repos,
++# their STATUS.json.
++set -euo pipefail
++OUT="$(cd "$(dirname "$0")/../.." && pwd)/docs/roadmap-baseline/data/github.json"
++mkdir -p "$(dirname "$OUT")"
++gh repo list OpenVibers --limit 200 --json name,description,visibility,pushedAt,diskUsage,defaultBranchRef,isArchived \
++ > "$OUT.repos"
++node - "$OUT.repos" > "$OUT.tmp" <<'NODE'
++const { execFileSync } = require('child_process');
++const repos = JSON.parse(require('fs').readFileSync(process.argv[2], 'utf8'));
++for (const r of repos) {
++ r.defaultBranch = r.defaultBranchRef ? r.defaultBranchRef.name : null;
++ delete r.defaultBranchRef;
++ try {
++ const raw = execFileSync('gh', ['api', `repos/OpenVibers/${r.name}/contents/STATUS.json`, '-H', 'Accept: application/vnd.github.raw'],
++ { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
++ r.status = JSON.parse(raw);
++ } catch { r.status = null; }
++}
++repos.sort((a, b) => a.name.localeCompare(b.name));
++process.stdout.write(JSON.stringify({ owner: 'OpenVibers', collectedAt: new Date().toISOString(), repos }, null, 2) + '\n');
++NODE
++rm -f "$OUT.repos"
++mv "$OUT.tmp" "$OUT"
++echo "wrote $OUT"
ddiff --git a/scripts/roadmap-baseline/collect-prod.sh b/scripts/roadmap-baseline/collect-prod.sh
nnew file mode 100755
iindex 00000000..06e36000
---- /dev/null
++++ b/scripts/roadmap-baseline/collect-prod.sh
@@@ -0,0 +1,51 @@
++#!/usr/bin/env bash
++# Read-only snapshot of the production host for the Wave 0 baseline.
++#
++# scripts/roadmap-baseline/collect-prod.sh [ssh-host] (default: openvibe-ovh)
++#
++# Writes docs/roadmap-baseline/data/prod-snapshot.json. What it records, and nothing more:
++# - deployed git SHA per /opt/openvibe.<svc>
++# - openvibe-* systemd unit names and state
++# - every SQLite file under /opt/openvibe.*: size and TABLE NAMES (sqlite3 -readonly; no rows read)
++# - env file names under /etc/openvibe and the variable NAMES they set. Values are stripped by
++# sed on the host before anything leaves it; they are never transferred or printed.
++set -euo pipefail
++HOST="${1:-openvibe-ovh}"
++OUT="$(cd "$(dirname "$0")/../.." && pwd)/docs/roadmap-baseline/data/prod-snapshot.json"
++mkdir -p "$(dirname "$OUT")"
++
++ssh -o BatchMode=yes "$HOST" 'bash -s' > "$OUT.tmp" <<'REMOTE'
++set -u
++node - <<'NODE'
++const { execSync } = require('child_process');
++const fs = require('fs');
++const sh = (c) => { try { return execSync(c, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return ''; } };
++const deployments = {};
++for (const d of fs.readdirSync('/opt').filter(n => n.startsWith('openvibe.'))) {
++ const dir = '/opt/' + d;
++ deployments[d.slice('openvibe.'.length)] = {
++ path: dir,
++ sha: sh(`git -C ${dir} rev-parse HEAD`) || null,
++ committedAt: sh(`git -C ${dir} log -1 --format=%cI`) || null,
++ };
++}
++const units = sh("systemctl list-units 'openvibe*' --all --no-legend --plain").split('\n').filter(Boolean)
++ .map(l => { const [unit, load, active, sub] = l.split(/\s+/); return { unit, load, active, sub }; });
++const databases = sh("find /opt/openvibe.* -maxdepth 5 -name '*.db' -not -path '*/node_modules/*'").split('\n').filter(Boolean)
++ .map(file => ({
++ file,
++ bytes: fs.statSync(file).size,
++ tables: sh(`sqlite3 -readonly '${file}' "select name from sqlite_master where type='table' and name not like 'sqlite_%' order by name"`).split('\n').filter(Boolean),
++ }));
++const envFiles = {};
++for (const f of sh('sudo -n ls /etc/openvibe').split('\n').filter(n => n.endsWith('.env'))) {
++ envFiles[f] = sh(`sudo -n sed -n -E 's/^[[:space:]]*(export[[:space:]]+)?([A-Za-z_][A-Za-z0-9_]*)=.*/\\2/p' /etc/openvibe/${f}`)
++ .split('\n').filter(Boolean).sort();
++}
++process.stdout.write(JSON.stringify({
++ host: sh('hostname'), collectedAt: new Date().toISOString(), deployments, units, databases, envFiles,
++}, null, 2) + '\n');
++NODE
++REMOTE
++mv "$OUT.tmp" "$OUT"
++echo "wrote $OUT"
ddiff --git a/scripts/roadmap-baseline/extract.js b/scripts/roadmap-baseline/extract.js
nnew file mode 100644
iindex 00000000..ea47a360
---- /dev/null
++++ b/scripts/roadmap-baseline/extract.js
@@@ -0,0 +1,270 @@
++'use strict';
++/**
++ * Static extractors for the Wave 0 baseline (docs/roadmap-baseline/).
++ *
++ * Every function here reads source text only — nothing is executed, required or connected to.
++ * The results are line-level pattern matches, so they are an inventory to review, not proof of
++ * behaviour: a route string inside a comment still counts, and a route built at runtime does not.
++ */
++const fs = require('fs');
++const path = require('path');
++
++const SOURCE_EXT = new Set(['.js', '.cjs', '.mjs', '.ts', '.sql']);
++const SKIP_DIRS = new Set(['node_modules', 'vendor', 'public', 'dist', 'data', 'scratch', '.git', 'coverage', 'browser-extension', 'hardware']);
++
++function walk(dir, out = []) {
++ let entries;
++ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return out; }
++ for (const e of entries) {
++ if (e.name.startsWith('.') && e.name !== '.') continue;
++ const full = path.join(dir, e.name);
++ if (e.isDirectory()) {
++ if (!SKIP_DIRS.has(e.name)) walk(full, out);
++ } else if (SOURCE_EXT.has(path.extname(e.name)) && !e.name.endsWith('.d.ts')) {
++ out.push(full);
++ }
++ }
++ return out;
++}
++
++const isTestFile = (rel) => /(^|\/)(test|tests|__tests__)\//.test(rel) || /\.(test|spec)\.[cm]?[jt]s$/.test(rel);
++
++// ── Tables ────────────────────────────────────────────────────────────────
++const TABLE_RE = /CREATE\s+(?:VIRTUAL\s+)?TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"[]?(\w+)[`"\]]?\s*(?:\(|USING\b)/gi;
++
++function extractTables(text) {
++ const out = [];
++ const lines = text.split('\n');
++ lines.forEach((line, i) => {
++ TABLE_RE.lastIndex = 0;
++ let m;
++ while ((m = TABLE_RE.exec(line))) out.push({ table: m[1], line: i + 1 });
++ });
++ return out;
++}
++
++// ── Express-style routes and mounts ───────────────────────────────────────
++const ROUTE_RE = /\b(\w+)\.(get|post|put|patch|delete|all)\(\s*(['"`])(\/[^'"`]*)\3/g;
++const MOUNT_RE = /\b(\w+)\.use\(\s*(['"`])(\/[^'"`]*)\2\s*,\s*([^\n]*)/g;
++// `const x = require('./x')`, `const x = require('./x').create(...)`, `const { a, b } = require('./x')`
++const REQUIRE_ASSIGN_RE = /\b(?:const|let|var)\s+(\{[^}]*\}|\w+)\s*=\s*require\(\s*['"](\.[^'"]+)['"]\s*\)/g;
++const INLINE_REQUIRE_RE = /require\(\s*['"](\.[^'"]+)['"]\s*\)/;
++// Receivers that are clearly not HTTP routers (Map#get with a path-like key etc.).
++const NOT_ROUTERS = new Set(['map', 'cache', 'headers', 'params', 'searchParams', 'url', 'Map', 'store', 'config', 'settings', 'res', 'req']);
++
++function extractRoutes(text) {
++ const out = [];
++ text.split('\n').forEach((line, i) => {
++ ROUTE_RE.lastIndex = 0;
++ let m;
++ while ((m = ROUTE_RE.exec(line))) {
++ if (NOT_ROUTERS.has(m[1])) continue;
++ out.push({ method: m[2].toUpperCase(), path: m[4], line: i + 1 });
++ }
++ });
++ return out;
++}
++
++function extractMounts(text) {
++ const requires = {};
++ let m;
++ REQUIRE_ASSIGN_RE.lastIndex = 0;
++ while ((m = REQUIRE_ASSIGN_RE.exec(text))) {
++ const names = m[1].startsWith('{') ? m[1].slice(1, -1).split(',').map(n => n.split(':').pop().trim()).filter(Boolean) : [m[1]];
++ for (const n of names) requires[n] = m[2];
++ }
++ const out = [];
++ text.split('\n').forEach((line, i) => {
++ MOUNT_RE.lastIndex = 0;
++ while ((m = MOUNT_RE.exec(line))) {
++ const rest = m[4];
++ const inline = rest.match(INLINE_REQUIRE_RE);
++ let spec = inline ? inline[1] : null;
++ if (!spec) {
++ // The router is the last argument that traces back to a require(): `r`, `svc.router`,
++ // `createRoutes(db, ...)`. Middleware (rateLimit(...), requireAuth) comes first.
++ const idents = (rest.match(/\b\w+\b/g) || []).filter(t => requires[t]);
++ if (idents.length) spec = requires[idents[idents.length - 1]];
++ }
++ out.push({ prefix: m[3], spec, line: i + 1 });
++ }
++ });
++ return out;
++}
++
++function resolveModule(fromFile, spec) {
++ if (!spec) return null;
++ const base = path.resolve(path.dirname(fromFile), spec);
++ for (const c of [base, base + '.js', base + '.ts', path.join(base, 'index.js'), path.join(base, 'routes.js')]) {
++ try { if (fs.statSync(c).isFile()) return c; } catch { /* next */ }
++ }
++ return null;
++}
++
++function joinPath(prefix, p) {
++ if (!prefix || prefix === '/') return p;
++ if (p === '/') return prefix;
++ return prefix.replace(/\/$/, '') + p;
++}
++
++/**
++ * Compose mount prefixes: a file mounted at /api/x by index.js gets /api/x in front of each of its
++ * routes; a file mounted by nobody is a root (prefix ''). Cycles are cut.
++ */
++function computePrefixes(mountsByFile) {
++ const parents = {}; // child -> [{parent, prefix}]
++ for (const [file, mounts] of Object.entries(mountsByFile)) {
++ for (const mt of mounts) {
++ if (!mt.target) continue;
++ (parents[mt.target] ||= []).push({ parent: file, prefix: mt.prefix });
++ }
++ }
++ const memo = {};
++ function prefixesOf(file, seen = new Set()) {
++ if (memo[file]) return memo[file];
++ if (seen.has(file)) return [''];
++ seen.add(file);
++ const ps = parents[file];
++ const result = !ps ? [''] : [...new Set(ps.flatMap(({ parent, prefix }) =>
++ prefixesOf(parent, seen).map(pp => joinPath(pp, prefix))))];
++ memo[file] = result;
++ return result;
++ }
++ return { prefixesOf, isMounted: (f) => Boolean(parents[f]) };
++}
++
++// ── Raw http.Server routing (OpenVibe.Games) ──────────────────────────────
++const URL_EQ_RE = /\burl\s*===\s*'(\/[^']*)'/g;
++const URL_STARTS_RE = /\burl\.startsWith\(\s*'(\/[^']*)'\s*\)/g;
++const METHOD_RE = /req\.method\s*===\s*'([A-Z]+)'/;
++
++function extractRawRoutes(text) {
++ const out = [];
++ const lines = text.split('\n');
++ lines.forEach((line, i) => {
++ const window = lines.slice(i, i + 3).join(' ');
++ const mm = window.match(METHOD_RE);
++ for (const [re, suffix] of [[URL_EQ_RE, ''], [URL_STARTS_RE, '*']]) {
++ re.lastIndex = 0;
++ let m;
++ while ((m = re.exec(line))) out.push({ method: mm ? mm[1] : 'ANY', path: m[1] + suffix, line: i + 1 });
++ }
++ });
++ return out;
++}
++
++// ── WebSockets ────────────────────────────────────────────────────────────
++const WSS_RE = /new\s+(?:WebSocket\.Server|WebSocketServer|WebSocket\.WebSocketServer)\s*\(/;
++const WS_PATH_RE = /['"`](\/ws\/[\w\-/]*|\/ws)['"`]/g;
++
++function extractWebSockets(text) {
++ const servers = [];
++ const paths = [];
++ text.split('\n').forEach((line, i) => {
++ if (WSS_RE.test(line)) servers.push({ line: i + 1 });
++ WS_PATH_RE.lastIndex = 0;
++ let m;
++ while ((m = WS_PATH_RE.exec(line))) paths.push({ path: m[1], line: i + 1 });
++ });
++ return { servers, paths };
++}
++
++// ── Environment variable names ───────────────────────────────────────────
++// process.env.X, plus env.X / env['X'] for code that receives the environment as a parameter (Games).
++const ENV_RE = /\b(?:process\.)?env\.([A-Z][A-Z0-9_]+)|\b(?:process\.)?env\[\s*['"]([A-Z][A-Z0-9_]+)['"]\s*\]/g;
++
++function extractEnv(text) {
++ const names = new Set();
++ let m;
++ ENV_RE.lastIndex = 0;
++ while ((m = ENV_RE.exec(text))) names.add(m[1] || m[2]);
++ return [...names];
++}
++
++/** Names that carry credentials. Classification only — values are never read. */
++function isSecretName(name) {
++ if (/PUBLIC|_PATH$|_FILE$|_DIR$|_TTL|_MS$|_SECONDS$|_MINUTES$|_ID$|_URL$|_HOST$|_PORT$|_MODE$|_ENABLED$|_LIMIT$/.test(name)
++ && !/WEBHOOK_URL$|SECRET|PASSWORD|PRIVATE/.test(name)) return false;
++ return /SECRET|PASSWORD|PASSWD|TOKEN|PRIVATE|CREDENTIAL|(^|_)KEY($|_)|APIKEY|WEBHOOK|COOKIE/.test(name);
++}
++
++// ── Cross-service calls ──────────────────────────────────────────────────
++const SERVICE_PORTS = { 4000: 'network', 4001: 'tools', 4100: 'media', 3000: 'live', 8000: 'games' };
++for (let p = 4010; p <= 4016; p++) SERVICE_PORTS[p] = 'tools';
++
++function envTarget(name) {
++ const m = name.match(/^(?:OV_)?(NETWORK|MEDIA|LIVE|TOOLS|GAMES|COMMUNITY)_(?:INTERNAL_)?(?:URL|API_URL|BASE_URL|ORIGIN)$/);
++ return m ? m[1].toLowerCase() : null;
++}
++
++const PATH_TARGETS = [
++ [/\/oauth\/token\b/, 'network'], [/\/api\/auth\/me\b/, 'network'], [/\/api\/\.well-known\/jwks/, 'network'],
++ [/\/internal\/(coins|notifications|events)\b/, 'network'], [/\/api\/v1\/\$\{/, 'media'],
++];
++
++const HTTP_CLIENT_RE = /\bfetch\(|\bhttps?\.request\(|\bhttps?\.get\(|\baxios\b|\bgot\(|\bundici\b|egress\.\w+\(/;
++
++function extractOutbound(text, selfService) {
++ if (!HTTP_CLIENT_RE.test(text)) return null;
++ const targets = new Map(); // service -> [{via, line}]
++ const add = (svc, via, line) => {
++ if (!svc || svc === selfService) return;
++ if (!targets.has(svc)) targets.set(svc, []);
++ targets.get(svc).push({ via, line });
++ };
++ text.split('\n').forEach((line, i) => {
++ let m;
++ const envRe = /\b(?:process\.)?env\.([A-Z][A-Z0-9_]+)/g;
++ while ((m = envRe.exec(line))) add(envTarget(m[1]), m[1], i + 1);
++ // Config indirection: config.networkInternalUrl, cfg.media.url, liveInternalUrl, ...
++ const identRe = /\b(network|media|live|tools|games|community)(Internal)?(Url|URL|Base|Origin)\b|\b(network|media|live|tools|games|community)\.(url|internalUrl|baseUrl|apiUrl)\b/gi;
++ while ((m = identRe.exec(line))) add((m[1] || m[4]).toLowerCase(), m[0], i + 1);
++ if (/webhook_?url/i.test(line)) add('webhook', 'webhook_url (per-app, configured in DB)', i + 1);
++ // Contract paths that only one service serves (CONTRACTS.md), for URLs built from config objects.
++ for (const [re, svc] of PATH_TARGETS) if (re.test(line)) add(svc, line.match(re)[0], i + 1);
++ const portRe = /(?:127\.0\.0\.1|localhost):(\d{4,5})/g;
++ while ((m = portRe.exec(line))) add(SERVICE_PORTS[m[1]], `:${m[1]}`, i + 1);
++ });
++ if (!targets.size) return null;
++ const auth = [];
++ if (/X-Internal-Key|INTERNAL_API_KEY|x-internal-key/.test(text)) auth.push('internal-key');
++ if (/MEDIA_API_KEY|X-Media-Key|x-api-key/i.test(text)) auth.push('api-key');
++ if (/Authorization['"]?\s*:\s*[`'"]Bearer|Bearer \$\{/.test(text)) auth.push('bearer');
++ if (/createHmac\(/.test(text)) auth.push('hmac');
++ if (/client_secret|OAUTH_CLIENT_SECRET/.test(text)) auth.push('oauth-client');
++ return {
++ targets: [...targets.entries()].map(([service, refs]) => ({ service, refs })),
++ auth: auth.length ? auth : ['none-detected'],
++ timeout: /AbortSignal\.timeout|\btimeout\s*:|AbortController/.test(text),
++ retry: /\bretr(y|ies|ied)\b|\battempts?\b|backoff/i.test(text),
++ };
++}
++
++// ── Background jobs ──────────────────────────────────────────────────────
++const JOB_RE = /jobs\.(every|singleFlight)\(\s*['"]([^'"]+)['"](?:\s*,\s*([\d_]+))?/g;
++const INTERVAL_RE = /\bsetInterval\(/;
++
++function extractJobs(text) {
++ const out = [];
++ text.split('\n').forEach((line, i) => {
++ JOB_RE.lastIndex = 0;
++ let m;
++ while ((m = JOB_RE.exec(line))) {
++ out.push({ kind: m[1] === 'every' ? 'jobs.every' : 'jobs.singleFlight', name: m[2],
++ intervalMs: m[3] ? Number(m[3].replace(/_/g, '')) : null, line: i + 1 });
++ }
++ if (INTERVAL_RE.test(line) && !/clearInterval/.test(line)) {
++ const lit = line.match(/,\s*([\d_]+)\s*(?:\*\s*([\d_]+))?\s*(?:\*\s*([\d_]+))?\s*\)/);
++ let ms = null;
++ if (lit) ms = [lit[1], lit[2], lit[3]].filter(Boolean).reduce((a, b) => a * Number(b.replace(/_/g, '')), 1);
++ out.push({ kind: 'setInterval', name: null, intervalMs: ms, line: i + 1 });
++ }
++ });
++ return out;
++}
++
++module.exports = {
++ walk, isTestFile, extractTables, extractRoutes, extractMounts, resolveModule, computePrefixes,
++ joinPath, extractRawRoutes, extractWebSockets, extractEnv, isSecretName, envTarget,
++ extractOutbound, extractJobs, SERVICE_PORTS,
++};
ddiff --git a/scripts/roadmap-baseline/generate.js b/scripts/roadmap-baseline/generate.js
nnew file mode 100644
iindex 00000000..20c1dd30
---- /dev/null
++++ b/scripts/roadmap-baseline/generate.js
@@@ -0,0 +1,507 @@
++#!/usr/bin/env node
++'use strict';
++/**
++ * Wave 0 baseline generator (roadmap section 5, Wave 0; section 12 items 1-3).
++ *
++ * node scripts/roadmap-baseline/generate.js regenerate docs/roadmap-baseline/*
++ * node scripts/roadmap-baseline/generate.js --check also exit 1 if any exit criterion fails
++ *
++ * Inputs
++ * - the sibling repo checkouts under OPENVIBE_ROOT (default: the directory holding this repo)
++ * - docs/roadmap-baseline/data/prod-snapshot.json (collect-prod.sh, read-only SSH)
++ * - docs/roadmap-baseline/data/github.json (collect-github.sh)
++ * - docs/roadmap-baseline/data/{services,ownership-rules,hazards,d-status}.json (hand-maintained)
++ * Output is deterministic for the same inputs so diffs show real change.
++ */
++const fs = require('fs');
++const path = require('path');
++const crypto = require('crypto');
++const { execFileSync } = require('child_process');
++const X = require('./extract');
++
++const NETWORK_DIR = path.resolve(__dirname, '../..');
++const ROOT = path.resolve(process.env.OPENVIBE_ROOT || path.join(NETWORK_DIR, '..'));
++const OUT_DIR = path.join(NETWORK_DIR, 'docs/roadmap-baseline');
++const DATA = path.join(OUT_DIR, 'data');
++const CHECK = process.argv.includes('--check');
++
++const REPOS = [
++ { name: 'OpenVibe.Live', service: 'live', scan: ['server'] },
++ { name: 'OpenVibe.Network', service: 'network', scan: ['server', 'packages/openvibe-shared'] },
++ { name: 'OpenVibe.Media', service: 'media', scan: ['server'] },
++ { name: 'OpenVibe.Tools', service: 'tools', scan: ['apps'] },
++ { name: 'OpenVibe.Community', service: 'community', scan: ['server'] },
++ { name: 'OpenVibe.Games', service: 'games', scan: ['apps/server/src', 'packages'] },
++ { name: 'OpenVibe.Sites', service: 'sites', scan: ['build.js'] },
++];
++const CANONICAL_SHARED = path.join(NETWORK_DIR, 'packages/openvibe-shared');
++
++const readJson = (f, fallback) => { try { return JSON.parse(fs.readFileSync(f, 'utf8')); } catch { return fallback; } };
++const git = (dir, ...args) => { try { return execFileSync('git', ['-C', dir, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return null; } };
++const sortBy = (arr, ...keys) => arr.sort((a, b) => { for (const k of keys) { const c = String(a[k] ?? '').localeCompare(String(b[k] ?? ''), 'en', { numeric: true }); if (c) return c; } return 0; });
++const esc = (s) => String(s ?? '').replace(/\|/g, '\\|').replace(/\n/g, ' ');
++const table = (head, rows) => rows.length
++ ? [`| ${head.join(' | ')} |`, `|${head.map(() => '---').join('|')}|`, ...rows.map(r => `| ${r.map(esc).join(' | ')} |`)].join('\n')
++ : '_none_';
++const fmtBytes = (n) => n >= 1e9 ? (n / 1e9).toFixed(2) + ' GB' : n >= 1e6 ? (n / 1e6).toFixed(1) + ' MB' : n >= 1e3 ? (n / 1e3).toFixed(0) + ' KB' : n + ' B';
++
++const services = readJson(path.join(DATA, 'services.json'), { services: [] }).services;
++const rules = readJson(path.join(DATA, 'ownership-rules.json'), { rules: [] }).rules;
++const hazards = readJson(path.join(DATA, 'hazards.json'), { hazards: [] });
++const dstatus = readJson(path.join(DATA, 'd-status.json'), { families: [] }).families;
++const prod = readJson(path.join(DATA, 'prod-snapshot.json'), null);
++const github = readJson(path.join(DATA, 'github.json'), null);
++
++// ── 1. Scan repos ─────────────────────────────────────────────────────────
++function scanRepo(cfg) {
++ const dir = path.join(ROOT, cfg.name);
++ const repo = { name: cfg.name, service: cfg.service, present: fs.existsSync(dir) };
++ if (!repo.present) return repo;
++ repo.git = {
++ head: git(dir, 'rev-parse', 'HEAD'), branch: git(dir, 'rev-parse', '--abbrev-ref', 'HEAD'),
++ originMain: git(dir, 'rev-parse', 'origin/main'), committedAt: git(dir, 'log', '-1', '--format=%cI'),
++ };
++ const files = cfg.scan.flatMap(s => {
++ const p = path.join(dir, s);
++ try { return fs.statSync(p).isFile() ? [p] : X.walk(p); } catch { return []; }
++ });
++ const rel = (f) => path.relative(dir, f);
++ const source = files.filter(f => !X.isTestFile(rel(f)));
++ repo.sourceFiles = source.length;
++ repo.loc = 0;
++ repo.tables = []; repo.routes = []; repo.ws = { servers: [], paths: [] };
++ repo.env = {}; repo.outbound = []; repo.jobs = []; repo.contractTodos = 0;
++ const mountsByFile = {}; const routesByFile = {}; const texts = {};
++
++ for (const f of source) {
++ const text = fs.readFileSync(f, 'utf8');
++ texts[f] = text;
++ const r = rel(f);
++ repo.loc += text.split('\n').length;
++ repo.contractTodos += (text.match(/TODO\(contract/g) || []).length;
++ for (const t of X.extractTables(text)) repo.tables.push({ table: t.table, file: r, line: t.line, via: 'repo' });
++ const mounts = X.extractMounts(text).map(m => ({ ...m, target: X.resolveModule(f, m.spec) }));
++ if (mounts.length) mountsByFile[f] = mounts;
++ const routes = cfg.service === 'games' ? X.extractRawRoutes(text) : X.extractRoutes(text);
++ if (routes.length) routesByFile[f] = routes;
++ const ws = X.extractWebSockets(text);
++ ws.servers.forEach(s => repo.ws.servers.push({ file: r, line: s.line }));
++ ws.paths.forEach(p => repo.ws.paths.push({ path: p.path, file: r, line: p.line }));
++ for (const name of X.extractEnv(text)) (repo.env[name] ||= []).push(r);
++ const ob = X.extractOutbound(text, cfg.service);
++ if (ob) repo.outbound.push({ file: r, ...ob });
++ for (const j of X.extractJobs(text)) repo.jobs.push({ ...j, file: r });
++ }
++
++ // Tables created by the vendored shared package (analytics etc.) belong to this service's DB.
++ const vendored = path.join(dir, 'vendor/openvibe-shared');
++ if (fs.existsSync(vendored)) {
++ for (const f of X.walk(vendored).filter(f => !X.isTestFile(path.relative(vendored, f)))) {
++ const text = fs.readFileSync(f, 'utf8');
++ for (const t of X.extractTables(text)) repo.tables.push({ table: t.table, file: rel(f), line: t.line, via: 'vendored openvibe-shared' });
++ const ob = X.extractOutbound(text, cfg.service);
++ if (ob) repo.outbound.push({ file: rel(f), ...ob });
++ for (const name of X.extractEnv(text)) (repo.env[name] ||= []).push(rel(f));
++ }
++ }
++
++ const { prefixesOf } = X.computePrefixes(mountsByFile);
++ for (const [f, routes] of Object.entries(routesByFile)) {
++ for (const rt of routes) {
++ for (const p of prefixesOf(f)) repo.routes.push({ method: rt.method, path: X.joinPath(p, rt.path), file: rel(f), line: rt.line });
++ }
++ }
++ const seen = new Set();
++ repo.routes = sortBy(repo.routes.filter(r => { const k = `${r.method} ${r.path} ${r.file}:${r.line}`; if (seen.has(k)) return false; seen.add(k); return true; }), 'path', 'method');
++ repo.unresolvedMounts = Object.entries(mountsByFile).flatMap(([f, ms]) => ms.filter(m => !m.target && m.spec).map(m => ({ file: rel(f), line: m.line, prefix: m.prefix, spec: m.spec })));
++
++ // Tests and CI: count across the whole repo, not only scanned dirs.
++ const allFiles = X.walk(dir);
++ repo.testFiles = allFiles.filter(f => X.isTestFile(rel(f))).length;
++ repo.ci = fs.existsSync(path.join(dir, '.github/workflows'));
++ return repo;
++}
++
++const repos = REPOS.map(scanRepo);
++const byName = Object.fromEntries(repos.map(r => [r.name, r]));
++const repoOfService = Object.fromEntries(REPOS.map(r => [r.service, r.name]));
++
++// ── 2. Vendored shared-package drift ────────────────────────────────────
++const sha1 = (f) => crypto.createHash('sha1').update(fs.readFileSync(f)).digest('hex');
++const vendored = [];
++for (const r of repos) {
++ const vdir = path.join(ROOT, r.name, 'vendor/openvibe-shared');
++ if (!fs.existsSync(vdir)) continue;
++ const files = X.walk(vdir).concat(fs.readdirSync(vdir).filter(n => /\.(css|json)$/.test(n)).map(n => path.join(vdir, n)));
++ for (const f of [...new Set(files)]) {
++ const relPath = path.relative(vdir, f);
++ if (relPath.startsWith('test/') || relPath === 'package.json') continue;
++ const canon = path.join(CANONICAL_SHARED, relPath);
++ const state = !fs.existsSync(canon) ? 'not-in-canonical' : sha1(canon) === sha1(f) ? 'identical' : 'divergent';
++ vendored.push({ repo: r.name, file: relPath, state });
++ }
++}
++sortBy(vendored, 'repo', 'file');
++
++// ── 3. Table ownership ──────────────────────────────────────────────────
++function classify(repo, tableName, dbs = []) {
++ for (const rule of rules) {
++ if (rule.repo !== '*' && rule.repo !== repo) continue;
++ if (rule.db && !dbs.includes(rule.db)) continue;
++ if (new RegExp(rule.match).test(tableName)) return rule;
++ }
++ return null;
++}
++const prodDbsByRepo = {};
++if (prod) {
++ for (const db of prod.databases) {
++ const svc = (db.file.match(/^\/opt\/openvibe\.([a-z]+)\//) || [])[1];
++ const repo = repoOfService[svc];
++ (prodDbsByRepo[repo || 'unknown'] ||= []).push(db);
++ }
++}
++const ownership = [];
++for (const r of repos.filter(r => r.present)) {
++ const declared = {};
++ for (const t of r.tables) (declared[t.table] ||= []).push(`${t.file}:${t.line}`);
++ const inProd = {};
++ // Backups are copies, not live schema; they are reported under discrepancies instead.
++ for (const db of (prodDbsByRepo[r.name] || []).filter(d => !/\/backups?\//.test(d.file))) for (const t of db.tables) (inProd[t] ||= []).push(path.basename(db.file));
++ for (const name of new Set([...Object.keys(declared), ...Object.keys(inProd)])) {
++ const inCode = Boolean(declared[name]);
++ const rule = inCode ? classify(r.name, name) : classify('prod-only', name, inProd[name]);
++ ownership.push({
++ repo: r.name, table: name, declaredAt: declared[name] || [], prodDbs: [...new Set(inProd[name] || [])],
++ inCode, inProd: Boolean(inProd[name]),
++ target: rule ? (rule.target === 'keep' ? r.name : rule.target) : 'UNCLASSIFIED',
++ disposition: rule ? rule.disposition : 'UNCLASSIFIED', wave: rule ? rule.wave : '', note: rule ? rule.note : '',
++ });
++ }
++}
++sortBy(ownership, 'repo', 'table');
++const unclassified = ownership.filter(o => o.disposition === 'UNCLASSIFIED');
++
++// ── 4. Cross-service calls ──────────────────────────────────────────────
++const calls = [];
++for (const r of repos) for (const ob of r.outbound || []) {
++ for (const t of ob.targets) {
++ calls.push({ caller: r.name, file: ob.file, target: repoOfService[t.service] || t.service,
++ via: [...new Set(t.refs.map(x => x.via))].join(', '), lines: t.refs.map(x => x.line).join(','),
++ auth: ob.auth.join(', '), timeout: ob.timeout, retry: ob.retry });
++ }
++}
++sortBy(calls, 'caller', 'target', 'file');
++
++// ── 5. Env names / secrets ──────────────────────────────────────────────
++const envFileOfRepo = { 'OpenVibe.Live': 'live.env', 'OpenVibe.Network': 'network.env', 'OpenVibe.Media': 'media.env',
++ 'OpenVibe.Tools': 'tools.env', 'OpenVibe.Community': 'community.env', 'OpenVibe.Games': 'games.env' };
++const prodEnv = prod ? prod.envFiles : {};
++const sharedEnv = new Set(prodEnv['generated-secrets.env'] || []);
++const envRows = [];
++for (const r of repos.filter(r => r.present)) {
++ const set = new Set(prodEnv[envFileOfRepo[r.name]] || []);
++ for (const [name, files] of Object.entries(r.env)) {
++ envRows.push({ repo: r.name, name, secret: X.isSecretName(name), files: [...new Set(files)].sort(),
++ prod: set.has(name) ? envFileOfRepo[r.name] : sharedEnv.has(name) ? 'generated-secrets.env' : null });
++ }
++ for (const name of set) {
++ if (!r.env[name]) envRows.push({ repo: r.name, name, secret: X.isSecretName(name), files: [], prod: envFileOfRepo[r.name], unreferenced: true });
++ }
++}
++sortBy(envRows, 'repo', 'name');
++
++// ── 6. Discrepancies (production vs repository) ─────────────────────────
++const discrepancies = [];
++const addD = (severity, area, subject, detail) => discrepancies.push({ severity, area, subject, detail });
++if (!prod) addD('unknown', 'production', 'all', 'prod-snapshot.json missing: run collect-prod.sh');
++else {
++ for (const s of services) {
++ const r = byName[s.repo];
++ const dep = prod.deployments[s.id];
++ if (!dep) { addD('high', 'deploy', s.id, `no /opt/openvibe.${s.id} on ${prod.host}`); continue; }
++ const target = r && (r.git.originMain || r.git.head);
++ if (dep.sha && target && dep.sha !== target && git(path.join(ROOT, s.repo), 'diff', '--quiet', dep.sha, target) === '') {
++ addD('low', 'deploy', s.id, `deployed ${dep.sha.slice(0, 7)} has the same file tree as origin/main ${target.slice(0, 7)} but a different SHA (pre-rewrite history); reset the checkout so later pulls fast-forward`);
++ } else if (dep.sha && target && dep.sha !== target) {
++ const behind = git(path.join(ROOT, s.repo), 'rev-list', '--count', `${dep.sha}..${target}`);
++ const ahead = git(path.join(ROOT, s.repo), 'rev-list', '--count', `${target}..${dep.sha}`);
++ addD('medium', 'deploy', s.id, behind === null
++ ? `deployed ${dep.sha.slice(0, 7)} is not in the local clone (unknown commit)`
++ : `deployed ${dep.sha.slice(0, 7)} is ${behind} commit(s) behind origin/main ${target.slice(0, 7)}${ahead && ahead !== '0' ? `, ${ahead} ahead` : ''}`);
++ }
++ if (r && r.git.originMain && r.git.head && r.git.head !== r.git.originMain) {
++ addD('info', 'checkout', s.repo, `local checkout ${r.git.head.slice(0, 7)} differs from origin/main ${r.git.originMain.slice(0, 7)}; scan reflects the checkout`);
++ }
++ const units = new Set(prod.units.map(u => u.unit));
++ for (const u of s.units) if (!units.has(u)) addD('medium', 'units', s.id, `expected unit ${u} not present`);
++ for (const u of prod.units.filter(u => u.unit.includes(`-${s.id}`))) {
++ if (u.active !== 'active') addD('high', 'units', s.id, `${u.unit} is ${u.active}/${u.sub}`);
++ }
++ }
++ const known = new Set(services.flatMap(s => s.units));
++ for (const u of prod.units) if (!known.has(u.unit)) addD('info', 'units', u.unit, 'unit not listed in services.json');
++ for (const db of prod.databases) {
++ if (db.tables.length === 0) addD('low', 'database', db.file, `${fmtBytes(db.bytes)}, zero tables (dead file)`);
++ if (/\/backups?\//.test(db.file)) addD('low', 'database', db.file, `${fmtBytes(db.bytes)} backup inside the service data dir`);
++ }
++ for (const o of ownership.filter(o => o.inProd && !o.inCode)) addD('low', 'schema', `${o.repo}:${o.table}`, `in production (${o.prodDbs.join(', ')}) but no CREATE TABLE in the repo; classified ${o.disposition} -> ${o.target}`);
++ for (const o of ownership.filter(o => o.inCode && !o.inProd && (prodDbsByRepo[o.repo] || []).length)) addD('info', 'schema', `${o.repo}:${o.table}`, 'declared in code but absent from every production database for this service (lazy/unused/legacy migration)');
++ for (const e of envRows.filter(e => e.unreferenced)) addD('info', 'env', `${e.repo}:${e.name}`, `set in ${e.prod} but not referenced by scanned code (may be read by a dependency or stale)`);
++ for (const e of envRows.filter(e => e.secret && !e.unreferenced && !e.prod)) addD('low', 'env', `${e.repo}:${e.name}`, 'secret-named variable read by code but not set in the service env file: verify it is optional or has no hardcoded fallback');
++}
++addD('unknown', 'provider', 'B2/R2/Cloudflare/PayPal consoles', 'console configuration (billing, lifecycle rules, zone settings, app config) is not visible from this environment');
++addD('unknown', 'host', 'powerchat.gg, raspi/hobo.tools (LAN)', 'unreachable from this environment; state not verified');
++const sevOrder = { high: 0, medium: 1, low: 2, info: 3, unknown: 4 };
++discrepancies.sort((a, b) => sevOrder[a.severity] - sevOrder[b.severity] || a.area.localeCompare(b.area) || a.subject.localeCompare(b.subject));
++
++// ── 7. Metrics, D-status verification, hazard checks ─────────────────────
++const charters = github ? github.repos.filter(r => r.status && r.status.code === false) : [];
++const metrics = {
++ 'github.charters': github ? `${charters.length} charter-only repos of ${github.repos.length}` : 'github.json missing',
++ 'contract.todo-markers': repos.map(r => `${r.name.replace('OpenVibe.', '')} ${r.contractTodos || 0}`).join(', '),
++ 'outbound.call-sites': `${calls.length} caller-file/target pairs across ${new Set(calls.map(c => c.file + c.caller)).size} files`,
++ 'outbound.internal-key': `${calls.filter(c => c.auth.includes('internal-key')).length} of ${calls.length} call sites authenticate with the shared internal key`,
++ 'ws.servers': `${repos.reduce((n, r) => n + (r.ws ? r.ws.servers.length : 0), 0)} WebSocket server constructions`,
++ 'tables.analytics-copies': `${new Set(ownership.filter(o => o.table === 'analytics_events').map(o => o.repo)).size} repos carry their own analytics_events`,
++ 'vendored.copies': `${new Set(vendored.map(v => v.repo)).size} repos vendor openvibe-shared (${vendored.length} files)`,
++ 'vendored.divergent': `${vendored.filter(v => v.state === 'divergent').length} divergent: ${vendored.filter(v => v.state === 'divergent').map(v => `${v.repo.replace('OpenVibe.', '')}/${v.file}`).join(', ') || 'none'}`,
++ 'ci.repos': `${repos.filter(r => r.ci).length} of ${repos.filter(r => r.present).length} scanned repos have .github/workflows`,
++ 'deploy.drift': `${discrepancies.filter(d => d.area === 'deploy').length} service(s) not running origin/main`,
++};
++for (const r of repos.filter(r => r.present)) {
++ metrics[`tests.${r.name}`] = `${r.testFiles} test files`;
++ metrics[`tables.${r.name}`] = `${ownership.filter(o => o.repo === r.name && o.inCode).length} tables`;
++ const targets = {};
++ for (const o of ownership.filter(o => o.repo === r.name)) (targets[o.target] ||= []).push(o.table);
++ for (const [t, names] of Object.entries(targets)) metrics[`tables.${r.name}.target:${t}`] = `${names.length} ${r.name.replace('OpenVibe.', '')} tables target ${t}`;
++}
++const metric = (k) => {
++ if (metrics[k] !== undefined) return metrics[k];
++ // Allow a family to name a target prefix (e.g. OpenVibe.Billing matches "OpenVibe.Billing (loyalty ledger)").
++ const hits = Object.keys(metrics).filter(m => m.startsWith(k + ' ') || m.startsWith(k));
++ return hits.length ? hits.map(h => metrics[h]).join('; ') : null;
++};
++const dRows = dstatus.map(fam => {
++ const ev = (fam.evidence || []).map(e => ({ ...e, ok: fs.existsSync(path.join(ROOT, e.repo, e.path)) }));
++ const ms = (fam.metrics || []).map(k => ({ key: k, value: metric(k) }));
++ const verified = ev.every(e => e.ok) && ms.every(m => m.value !== null);
++ return { ...fam, evidence: ev, metrics: ms, verified };
++});
++
++function runCheck(check) {
++ if (!check) return null;
++ if (check === 'live-socket-unit') {
++ const u = prod && prod.units.find(x => x.unit === 'openvibe-live.socket');
++ return u ? `openvibe-live.socket is ${u.active}/${u.sub}` : 'socket unit not found';
++ }
++ let m;
++ if ((m = check.match(/^env:([\w.-]+):(\w+)$/))) return (prodEnv[m[1]] || []).includes(m[2]) ? `${m[2]} is set in ${m[1]}` : `${m[2]} NOT set in ${m[1]}`;
++ if ((m = check.match(/^env-absent:([\w.-]+):(\w+)$/))) return (prodEnv[m[1]] || []).includes(m[2]) ? `${m[2]} is now set in ${m[1]}: re-verify` : `${m[2]} still unset in ${m[1]}`;
++ if (check === 'workstation-disk') {
++ try { return 'workstation / is ' + execFileSync('df', ['-P', '/'], { encoding: 'utf8' }).trim().split('\n')[1].split(/\s+/)[4] + ' used (at generation time)'; } catch { return null; }
++ }
++ if (check === 'commit-identity') {
++ const bad = [];
++ for (const r of repos.filter(r => r.present)) {
++ const ref = r.git.originMain ? 'origin/main' : 'HEAD';
++ const log = git(path.join(ROOT, r.name), 'log', ref, '--format=%ae%x09%B%x1e') || '';
++ let authors = 0, trailers = 0;
++ for (const entry of log.split('\x1e')) {
++ const [email, body = ''] = entry.trim().split('\t');
++ if (!email) continue;
++ if (email.toLowerCase() !== '[email protected]') authors++;
++ if (/^(co-authored-by:.*(claude|anthropic)|claude-session:)/im.test(body)) trailers++;
++ }
++ if (authors || trailers) bad.push(`${r.name.replace('OpenVibe.', '')} (${trailers} commits with assistant trailers${authors ? `, ${authors} other authors` : ''})`);
++ }
++ return bad.length ? `main history violates the rule in: ${bad.join('; ')}` : 'every scanned main: OpenVibers author, no assistant trailers';
++ }
++ if (check === 'db-size') {
++ return prod ? prod.databases.filter(d => d.tables.includes('analytics_events') && d.bytes > 50e6)
++ .map(d => `${d.file.replace('/opt/openvibe.', '')} ${fmtBytes(d.bytes)}`).join('; ') : null;
++ }
++ if (check === 'deploy-drift') return discrepancies.filter(d => d.area === 'deploy').map(d => `${d.subject}: ${d.detail}`).join('; ') || 'every service runs origin/main';
++ if (check === 'dead-dbs') return discrepancies.filter(d => d.area === 'database').map(d => d.subject.replace('/opt/openvibe.', '')).join(', ') || 'none';
++ return null;
++}
++const hazardRows = hazards.hazards.map(h => ({ ...h, checkResult: runCheck(h.check) }));
++
++// ── 8. Exit criteria ────────────────────────────────────────────────────
++const unresolvedMounts = repos.flatMap(r => (r.unresolvedMounts || []).map(m => ({ repo: r.name, ...m })));
++const callsWithoutTimeout = calls.filter(c => !c.timeout);
++const criteria = [
++ { id: 'EC1', text: 'Every table has an owner classification', pass: unclassified.length === 0,
++ detail: `${ownership.length} tables classified, ${unclassified.length} unclassified` },
++ { id: 'EC2', text: 'Every route is attributed to a repo and source line', pass: repos.every(r => !r.present || r.routes.every(x => x.file && x.line)),
++ detail: `${repos.reduce((n, r) => n + (r.routes || []).length, 0)} routes; ${unresolvedMounts.length} router mounts whose module could not be resolved (their routes are listed without the mount prefix)` },
++ { id: 'EC3', text: 'Every background job is listed with its location', pass: true,
++ detail: `${repos.reduce((n, r) => n + (r.jobs || []).length, 0)} timers/jobs` },
++ { id: 'EC4', text: 'Every cross-service call records caller, callee, auth, timeout and retry', pass: calls.length > 0,
++ detail: `${calls.length} call sites; ${callsWithoutTimeout.length} with no timeout detected` },
++ { id: 'EC5', text: 'Unknowns are marked unknown, not guessed', pass: true,
++ detail: `${discrepancies.filter(d => d.severity === 'unknown').length} unknown items recorded` },
++ { id: 'EC6', text: 'D01-D46 status resolved from evidence', pass: dRows.every(d => d.verified),
++ detail: `${dRows.filter(d => d.verified).length}/${dRows.length} families verified` },
++ { id: 'EC7', text: 'Hazard register reviewed by the production-host owner', pass: Boolean(hazards.reviewedBy),
++ detail: hazards.reviewedBy ? `reviewed by ${hazards.reviewedBy}` : 'pending: set reviewedBy in data/hazards.json after review' },
++];
++
++// ── 9. Write outputs ────────────────────────────────────────────────────
++const inventory = {
++ generatedFrom: Object.fromEntries(repos.filter(r => r.present).map(r => [r.name, r.git.head])),
++ prodSnapshot: prod ? { host: prod.host, collectedAt: prod.collectedAt } : null,
++ repos: repos.map(r => ({ ...r })), vendored, ownership, calls, env: envRows, discrepancies, metrics,
++ dStatus: dRows, hazards: hazardRows, criteria,
++};
++fs.mkdirSync(OUT_DIR, { recursive: true });
++fs.writeFileSync(path.join(OUT_DIR, 'inventory.json'), JSON.stringify(inventory, null, 1) + '\n');
++
++const header = (title, blurb) => `# ${title}\n\n> Generated by \`scripts/roadmap-baseline/generate.js\`. Do not edit by hand; edit \`data/*.json\` or the extractor and regenerate.\n\n${blurb}\n`;
++const loc = (repo, file, line) => `${repo.replace('OpenVibe.', '')}/${file}${line ? ':' + line : ''}`;
++const docs = {};
++
++docs['01-services.md'] = header('Services and repositories', 'Deliverable 1: runtimes, ports, units, env files and repository state.') + [
++ '\n## Running services\n',
++ table(['Service', 'Repo', 'Stage', 'Ports', 'Units', 'Env file', 'Deployed', 'Databases'], services.map(s => {
++ const dep = prod && prod.deployments[s.id];
++ return [s.id, s.repo, s.stage, Object.entries(s.ports).map(([k, v]) => `${k} ${Array.isArray(v) ? v.join('/') : v}`).join(', '),
++ s.units.join(', ') || '-', s.envFile || '-', dep && dep.sha ? dep.sha.slice(0, 7) : '-', s.databases.join(', ') || 'none'];
++ })),
++ '\n## Scanned repositories\n',
++ table(['Repo', 'HEAD', 'origin/main', 'Source files', 'LOC', 'Test files', 'CI', 'Routes', 'Tables', 'WS servers', 'Timers/jobs', 'TODO(contract)'],
++ repos.filter(r => r.present).map(r => [r.name, (r.git.head || '').slice(0, 7), (r.git.originMain || '').slice(0, 7), r.sourceFiles, r.loc,
++ r.testFiles, r.ci ? 'yes' : 'no', r.routes.length, new Set(r.tables.map(t => t.table)).size, r.ws.servers.length, r.jobs.length, r.contractTodos])),
++ '\nLOC counts non-test source files in the scanned directories only (`server/`, `apps/`, `packages/`), not frontend assets.\n',
++ '\n## GitHub account census\n',
++ github ? table(['Repository', 'Visibility', 'Stage (STATUS.json)', 'Last push', 'Size', 'Description'],
++ github.repos.map(r => [r.name, r.visibility, r.status ? `${r.status.stage}${r.status.code === false ? ' (no code)' : ''}` : '-', (r.pushedAt || '').slice(0, 10), `${r.diskUsage} KB`, r.description || '']))
++ + `\n\nCollected ${github.collectedAt.slice(0, 10)} via \`gh repo list OpenVibers\`.` : '_github.json missing: run collect-github.sh_',
++].join('\n') + '\n';
++
++docs['02-schema.md'] = header('Database schema inventory', 'Deliverable 2: every table declared in code or present in production, with current and target owner. Row data was never read.') + [
++ prod ? '\n## Production database files\n\n' + table(['File', 'Size', 'Tables'], prod.databases.map(d => [d.file, fmtBytes(d.bytes), d.tables.length])) : '',
++ ...repos.filter(r => r.present).map(r => {
++ const rows = ownership.filter(o => o.repo === r.name);
++ if (!rows.length) return `\n## ${r.name}\n\nNo tables.`;
++ return `\n## ${r.name} (${rows.length})\n\n` + table(['Table', 'Declared at', 'In prod', 'Target owner', 'Disposition', 'Wave'],
++ rows.map(o => [o.table, o.declaredAt.slice(0, 2).join(', ') + (o.declaredAt.length > 2 ? ` +${o.declaredAt.length - 2}` : '') || '(prod only)',
++ o.inProd ? o.prodDbs.join(', ') : 'no', o.target, o.disposition, o.wave]));
++ }),
++].join('\n') + '\n';
++
++docs['03-routes.md'] = header('HTTP route inventory', 'Deliverable 3 (routes half). Paths include the prefix of the `app.use()` mount that loads the router file; Games is matched from its raw `http.Server` handler. Static extraction: a route built at runtime is missed, and a route string in a comment is included.') + [
++ '\n## Summary\n',
++ table(['Repo', 'Routes', 'GET', 'POST', 'PUT/PATCH', 'DELETE', 'Other', '/internal/*'], repos.filter(r => r.present && r.routes.length).map(r => {
++ const c = (m) => r.routes.filter(x => m.includes(x.method)).length;
++ return [r.name, r.routes.length, c(['GET']), c(['POST']), c(['PUT', 'PATCH']), c(['DELETE']), c(['ALL', 'ANY']), r.routes.filter(x => x.path.startsWith('/internal')).length];
++ })),
++ unresolvedMounts.length ? '\n## Unresolved mounts\n\nRouters mounted with a prefix whose module the extractor could not resolve. Their routes appear below without that prefix.\n\n'
++ + table(['Repo', 'Mount', 'Module', 'At'], unresolvedMounts.map(m => [m.repo, m.prefix, m.spec, `${m.file}:${m.line}`])) : '',
++ ...repos.filter(r => r.present && r.routes.length).map(r => `\n## ${r.name}\n\n` + table(['Method', 'Path', 'Source'], r.routes.map(x => [x.method, x.path, `${x.file}:${x.line}`]))),
++].join('\n') + '\n';
++
++docs['04-cross-service.md'] = header('Cross-service calls', 'Deliverable 3 (dependency half). A call site is a source file that makes HTTP requests and names another service by internal URL env var or loopback port. Auth, timeout and retry are detected per file, so treat them as leads to confirm.') + [
++ '\n## Dependency graph\n',
++ table(['Caller', 'Callee', 'Files', 'Auth mechanisms'], Object.values(calls.reduce((acc, c) => {
++ const k = c.caller + '>' + c.target;
++ acc[k] ||= { caller: c.caller, target: c.target, files: 0, auth: new Set() };
++ acc[k].files++; c.auth.split(', ').forEach(a => acc[k].auth.add(a));
++ return acc;
++ }, {})).map(e => [e.caller, e.target, e.files, [...e.auth].sort().join(', ')])),
++ '\n## Call sites\n',
++ table(['Caller file', 'Callee', 'Via', 'Auth', 'Timeout', 'Retry'], calls.map(c => [loc(c.caller, c.file, c.lines.split(',')[0]), c.target, c.via, c.auth, c.timeout ? 'yes' : '**no**', c.retry ? 'yes' : 'no'])),
++ '\nInbound webhooks and internal endpoints are in [03-routes.md](03-routes.md) under `/internal/*` and `/api/webhooks/*`.',
++].join('\n') + '\n';
++
++docs['05-realtime-and-jobs.md'] = header('WebSockets, protocols and background jobs', 'Deliverable 4, plus the background-job half of the exit criteria.') + [
++ '\n## WebSocket servers\n',
++ table(['Repo', 'Constructed at'], repos.flatMap(r => (r.ws ? r.ws.servers : []).map(s => [r.name, `${s.file}:${s.line}`]))),
++ '\n## WebSocket paths referenced\n',
++ table(['Repo', 'Path', 'Referenced at'], repos.flatMap(r => {
++ const g = {};
++ for (const p of (r.ws ? r.ws.paths : [])) (g[p.path] ||= []).push(`${p.file}:${p.line}`);
++ return Object.entries(g).sort().map(([p, at]) => [r.name, p, at.slice(0, 3).join(', ') + (at.length > 3 ? ` +${at.length - 3}` : '')]);
++ })),
++ '\n## Non-HTTP protocols\n',
++ table(['Service', 'Protocol', 'Port(s)'], services.flatMap(s => Object.entries(s.ports).filter(([k]) => !['http', 'gateway', 'maps', 'food', 'img', 'yt', 'audio', 'text', 'docs'].includes(k))
++ .map(([k, v]) => [s.id, k, Array.isArray(v) ? v.join(', ') : v]))),
++ '\n## Background jobs and timers\n',
++ '`jobs.every` / `jobs.singleFlight` are Live\'s guarded job runner (`server/utils/jobs.js`); `setInterval` rows are raw timers.\n',
++ table(['Repo', 'Kind', 'Name', 'Interval', 'At'], repos.flatMap(r => (r.jobs || []).map(j => [r.name, j.kind, j.name || '', j.intervalMs ? (j.intervalMs >= 60000 ? `${+(j.intervalMs / 60000).toFixed(1)} min` : `${j.intervalMs / 1000} s`) : '?', `${j.file}:${j.line}`]))),
++].join('\n') + '\n';
++
++docs['06-secrets.md'] = header('Secrets and auth mechanisms', 'Deliverable 5. Names and locations only; no value was read. "In prod" names the `/etc/openvibe/*.env` file that sets the name.') + [
++ '\n## Auth mechanisms\n',
++ table(['Mechanism', 'Used by', 'Where'], [
++ ['User JWT (RS256, Network-issued, verified offline via JWKS)', 'all services', 'Network server/auth, CONTRACTS.md'],
++ ['OAuth2 client credentials per app (OV_OAUTH_CLIENT_ID/SECRET)', 'Live, Tools, Games, Media, Community', 'CONTRACTS.md'],
++ ['Shared internal key (X-Internal-Key / INTERNAL_API_KEY)', `${new Set(calls.filter(c => c.auth.includes('internal-key')).map(c => c.caller)).size} calling repos`, 'Network /internal/*, Live /internal/*'],
++ ['Per-app Media API key (Bearer)', 'Live, Community, Tools', 'Media apps table + MEDIA_APP_KEYS'],
++ ['HMAC webhook signatures', 'Media -> Live, PowerChat, Resend', 'X-OVMedia-Signature, Svix'],
++ ['hbt_ API tokens', 'Live bots/integrations', 'Live api_tokens (docs/api-tokens.md)'],
++ ['Stream keys', 'RTMP/WHIP publishers', 'Live managed_streams.stream_key (hazard H4)'],
++ ]),
++ '\n## Secret-named variables\n',
++ table(['Repo', 'Variable', 'In prod', 'Read at'], envRows.filter(e => e.secret).map(e => [e.repo, e.name, e.prod || (e.unreferenced ? '' : '**not set**'), e.files.slice(0, 3).join(', ') + (e.files.length > 3 ? ` +${e.files.length - 3}` : '') || '(not referenced)'])),
++ '\n## Env files on the production host\n',
++ table(['File', 'Variables'], Object.entries(prodEnv).map(([f, n]) => [`/etc/openvibe/${f}`, n.length])),
++ '\n## Other configuration variables\n',
++ table(['Repo', 'Variable', 'In prod', 'Read at'], envRows.filter(e => !e.secret).map(e => [e.repo, e.name, e.prod || '', e.files.slice(0, 2).join(', ') + (e.files.length > 2 ? ` +${e.files.length - 2}` : '') || '(not referenced)'])),
++].join('\n') + '\n';
++
++const moveRows = ownership.filter(o => o.target !== o.repo);
++docs['07-ownership.md'] = header('Data ownership: current vs target', 'Deliverable 6. Current owner = the repo that creates the table; target owner = roadmap sections 14.1 and 16, applied by `data/ownership-rules.json`.') + [
++ '\n## Summary\n',
++ table(['Current owner', 'Tables', 'Stay', 'Move', 'Frozen legacy', 'Investigate'], repos.filter(r => r.present).map(r => {
++ const rows = ownership.filter(o => o.repo === r.name);
++ return [r.name, rows.length, rows.filter(o => o.target === r.name).length, rows.filter(o => o.target !== r.name && o.disposition !== 'frozen-legacy' && o.disposition !== 'investigate').length,
++ rows.filter(o => o.disposition === 'frozen-legacy').length, rows.filter(o => o.disposition === 'investigate' || o.disposition === 'UNCLASSIFIED').length];
++ })),
++ '\n## Authority moves\n',
++ table(['Target authority', 'From', 'Tables', 'Wave'], Object.values(moveRows.reduce((acc, o) => {
++ const k = o.target + '<' + o.repo;
++ acc[k] ||= { target: o.target, from: o.repo, tables: [], wave: o.wave };
++ acc[k].tables.push(o.table);
++ return acc;
++ }, {})).sort((a, b) => a.target.localeCompare(b.target) || a.from.localeCompare(b.from)).map(e => [e.target, e.from, e.tables.join(', '), e.wave])),
++ unclassified.length ? '\n## Unclassified\n\n' + table(['Repo', 'Table'], unclassified.map(o => [o.repo, o.table])) : '\n## Unclassified\n\nNone.',
++].join('\n') + '\n';
++
++docs['08-discrepancies.md'] = header('Production vs repository', `Deliverable 7. Snapshot of ${prod ? prod.host + ' taken ' + prod.collectedAt.slice(0, 16).replace('T', ' ') + ' UTC' : '(no snapshot)'} compared with the local checkouts. \`unknown\` rows are things this environment cannot see.`) + '\n'
++ + table(['Severity', 'Area', 'Subject', 'Detail'], discrepancies.map(d => [d.severity, d.area, d.subject, d.detail])) + '\n';
++
++docs['09-d-status.md'] = header('D01-D46 status', 'Deliverable 8. Family-level status from roadmap 3.2. Evidence paths are checked on disk and metrics are recomputed from this scan each run; a family whose evidence does not resolve is marked unverified.') + '\n'
++ + table(['IDs', 'Family', 'Status', 'Disposition', 'Wave', 'Evidence', 'Measured'], dRows.map(d => [d.ids, d.name, d.status, d.disposition, d.wave,
++ d.evidence.map(e => `${e.ok ? '' : '**MISSING** '}${e.repo.replace('OpenVibe.', '')}/${e.path}`).join('; ') || '-',
++ [...d.metrics.map(m => m.value === null ? `**no metric ${m.key}**` : m.value), ...(d.hazards || []).map(h => `hazard ${h}`)].join('; ') || '-']))
++ + `\n\nTotals: ${dRows.filter(d => d.status === 'built').length} built, ${dRows.filter(d => d.status === 'partial').length} partial, ${dRows.filter(d => d.status === 'absent').length} absent across ${dRows.length} families; ${dRows.filter(d => d.verified).length} verified.\n\n## Findings\n\n`
++ + dRows.map(d => `- **${d.ids} ${d.name}.** ${d.finding}`).join('\n') + '\n';
++
++docs['10-hazards.md'] = header('Hazard register', `Deliverable 9. ${hazards.reviewedBy ? `Reviewed by ${hazards.reviewedBy}.` : '**Not yet reviewed by the production-host owner** (Wave 0 exit criterion EC7).'} "Check" is re-evaluated on every run.`) + '\n'
++ + table(['ID', 'Hazard', 'Severity', 'Status', 'Owner', 'Wave', 'Mitigation', 'Check'], hazardRows.map(h => [h.id, h.title, h.severity, h.status, h.owner, h.wave, h.mitigation, h.checkResult || h.evidence || '-']))
++ + '\n\n## Constraints\n\n' + hazardRows.map(h => `- **${h.id}** (${h.source}): ${h.constraint}${h.evidence ? ` _Evidence: ${h.evidence}_` : ''}`).join('\n') + '\n';
++
++docs['README.md'] = `# Wave 0 baseline\n\nThe audit baseline from the OpenVibe development roadmap (Wave 0; section 12 items 1-3): what exists across the estate, who owns each piece of data today and who should, what production looks like next to the repos, and which hazards constrain later waves. Every file here except \`data/*.json\` is generated.\n\n`
++ + `Source commits: ${Object.entries(inventory.generatedFrom).map(([k, v]) => `${k.replace('OpenVibe.', '')} \`${(v || '').slice(0, 7)}\``).join(', ')}. `
++ + `Production snapshot: ${prod ? `${prod.host}, ${prod.collectedAt.slice(0, 10)}` : 'missing'}.\n\n## Deliverables\n\n`
++ + table(['#', 'Artifact', 'Contents'], [
++ ['1', '[01-services.md](01-services.md)', 'services, ports, units, env files, repo state, GitHub census'],
++ ['2', '[02-schema.md](02-schema.md)', `${ownership.length} tables with current/target owner`],
++ ['3', '[03-routes.md](03-routes.md), [04-cross-service.md](04-cross-service.md)', `${repos.reduce((n, r) => n + (r.routes || []).length, 0)} routes; ${calls.length} cross-service call sites`],
++ ['4', '[05-realtime-and-jobs.md](05-realtime-and-jobs.md)', 'WebSocket servers and paths, non-HTTP protocols, background jobs'],
++ ['5', '[06-secrets.md](06-secrets.md)', 'auth mechanisms and secret names (no values)'],
++ ['6', '[07-ownership.md](07-ownership.md)', 'current vs target data ownership'],
++ ['7', '[08-discrepancies.md](08-discrepancies.md)', `${discrepancies.length} production-vs-repo items`],
++ ['8', '[09-d-status.md](09-d-status.md)', 'D01-D46 family status from evidence'],
++ ['9', '[10-hazards.md](10-hazards.md)', `${hazardRows.length} hazards with owners, mitigations and waves`],
++ ['-', '[inventory.json](inventory.json)', 'everything above, machine-readable'],
++ ])
++ + '\n\n## Exit criteria\n\n' + table(['', 'Criterion', 'Result'], criteria.map(c => [c.pass ? 'pass' : '**open**', c.text, c.detail]))
++ + `\n\n## Regenerating\n\n\`\`\`bash\nscripts/roadmap-baseline/collect-prod.sh # read-only SSH: deployed SHAs, units, DB table names, env var NAMES\nscripts/roadmap-baseline/collect-github.sh # gh: repo list + charter STATUS.json\nnode scripts/roadmap-baseline/generate.js # scan sibling checkouts, write this directory\nnode scripts/roadmap-baseline/generate.js --check # also exit 1 while an exit criterion is open\n\`\`\`\n\nThe generator reads sibling checkouts under \`OPENVIBE_ROOT\` (default: the parent of this repo). Keep them on \`main\` and pulled before regenerating.\n\n## What this baseline does not claim\n\n- Extraction is static pattern matching. Routes built at runtime, calls through helper wrappers without an internal URL or port, and tables created by dependencies outside \`vendor/openvibe-shared\` can be missed. Treat counts as a floor.\n- Timeout/retry/auth detection is per file, not per request. A module that exports several routers gets every mount prefix it is loaded under.\n- No production row data, env values, provider console or payment record was read.\n- \`Source.OpenVibe.Games\`, \`AFResume\` and \`BreakRoomSimulator\` are not scanned; the charter repos have no code to scan.\n`;
++
++for (const [f, body] of Object.entries(docs)) fs.writeFileSync(path.join(OUT_DIR, f), body);
++
++const open = criteria.filter(c => !c.pass);
++console.log(`roadmap-baseline: ${ownership.length} tables, ${repos.reduce((n, r) => n + (r.routes || []).length, 0)} routes, ${calls.length} call sites, ${discrepancies.length} discrepancies; ${criteria.length - open.length}/${criteria.length} exit criteria pass`);
++for (const c of open) console.log(` open ${c.id}: ${c.text} (${c.detail})`);
++if (CHECK && open.length) process.exit(1);
ddiff --git a/test/roadmap-baseline.test.js b/test/roadmap-baseline.test.js
nnew file mode 100644
iindex 00000000..0e0c99ee
---- /dev/null
++++ b/test/roadmap-baseline.test.js
@@@ -0,0 +1,82 @@
++'use strict';
++// Extractors behind docs/roadmap-baseline (scripts/roadmap-baseline/extract.js).
++const assert = require('assert');
++const fs = require('fs');
++const os = require('os');
++const path = require('path');
++const X = require('../scripts/roadmap-baseline/extract');
++
++// Tables: real CREATE TABLE statements only, not prose that happens to contain the words.
++assert.deepStrictEqual(X.extractTables([
++ "db.exec(`CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY)`);",
++ '// we CREATE TABLE below once the lock is held',
++ 'CREATE VIRTUAL TABLE docs_fts USING fts5(body);',
++ 'create table "quoted" (x)',
++].join('\n')).map(t => t.table), ['users', 'docs_fts', 'quoted']);
++
++// Routes: express verbs with a leading slash; Map#get and friends are ignored.
++assert.deepStrictEqual(X.extractRoutes([
++ "router.get('/a', h);",
++ "app.post(\"/b/:id\", requireAuth, h);",
++ "cache.get('/not-a-route');",
++ "r.delete(`/c`, h)",
++].join('\n')).map(r => `${r.method} ${r.path}`), ['GET /a', 'POST /b/:id', 'DELETE /c']);
++
++// Mount prefixes compose through required modules, including factory and destructured forms.
++const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'rb-'));
++const w = (f, body) => { fs.mkdirSync(path.dirname(path.join(tmp, f)), { recursive: true }); fs.writeFileSync(path.join(tmp, f), body); return path.join(tmp, f); };
++const index = w('index.js', [
++ "const chat = require('./chat/routes');",
++ "const svc = require('./svc').create({});",
++ "const { makeAdmin } = require('./admin');",
++ "app.use('/api/chat', chat);",
++ "app.use('/api/svc', rateLimit({ max: 5 }), svc.router);",
++ "app.use('/api/admin', makeAdmin(db, requireAuth));",
++ "app.use('/api/inline', require('./inline'));",
++].join('\n'));
++const chat = w('chat/routes.js', "const sub = require('./dm');\nrouter.get('/history', h);\nrouter.use('/dm', sub);");
++const dm = w('chat/dm.js', "router.post('/send', h);");
++const svc = w('svc.js', "router.get('/x', h);");
++const admin = w('admin.js', "router.get('/y', h);");
++const inline = w('inline.js', "router.get('/z', h);");
++const mountsByFile = {};
++for (const f of [index, chat]) mountsByFile[f] = X.extractMounts(fs.readFileSync(f, 'utf8')).map(m => ({ ...m, target: X.resolveModule(f, m.spec) }));
++const { prefixesOf } = X.computePrefixes(mountsByFile);
++assert.deepStrictEqual(prefixesOf(chat), ['/api/chat']);
++assert.deepStrictEqual(prefixesOf(dm), ['/api/chat/dm']);
++assert.deepStrictEqual(prefixesOf(svc), ['/api/svc']);
++assert.deepStrictEqual(prefixesOf(admin), ['/api/admin']);
++assert.deepStrictEqual(prefixesOf(inline), ['/api/inline']);
++assert.deepStrictEqual(prefixesOf(index), ['']);
++fs.rmSync(tmp, { recursive: true, force: true });
++
++// Raw http.Server routing (Games).
++assert.deepStrictEqual(X.extractRawRoutes([
++ "if (url === '/api/map' && req.method === 'POST') {",
++ "if (url.startsWith('/auth/')) {",
++].join('\n')).map(r => `${r.method} ${r.path}`), ['POST /api/map', 'ANY /auth/*']);
++
++// Env names: process.env and env-parameter forms; secret classification never needs a value.
++assert.deepStrictEqual(X.extractEnv("process.env.A_KEY; env.OV_NETWORK_URL; process.env['B_SECRET']").sort(), ['A_KEY', 'B_SECRET', 'OV_NETWORK_URL']);
++for (const n of ['INTERNAL_API_KEY', 'PAYPAL_CLIENT_SECRET', 'MEDIA_B2_SECRET_ACCESS_KEY', 'DISCORD_BOT_TOKEN', 'OPS_ALERT_WEBHOOK_URL']) assert.ok(X.isSecretName(n), n);
++for (const n of ['OV_NETWORK_URL', 'VAPID_PUBLIC_KEY', 'MEDIA_APP_ID', 'TOKEN_TTL_MS', 'WHISPER_MODEL_MULTI', 'KEY_FILE']) assert.ok(!X.isSecretName(n), n);
++
++// Cross-service calls: env var, loopback port, config indirection, contract path; self calls ignored.
++const ob = X.extractOutbound([
++ "const r = await fetch(`${process.env.OV_NETWORK_INTERNAL_URL}/internal/coins/credit`, {",
++ " headers: { 'X-Internal-Key': key }, signal: AbortSignal.timeout(5000) });",
++ "await fetch(config.media.url + '/x');",
++ "await fetch('http://127.0.0.1:3000/api/streams');",
++].join('\n'), 'live');
++assert.deepStrictEqual(ob.targets.map(t => t.service).sort(), ['media', 'network']);
++assert.deepStrictEqual(ob.auth, ['internal-key']);
++assert.strictEqual(ob.timeout, true);
++assert.strictEqual(X.extractOutbound("const u = process.env.OV_NETWORK_URL;", 'live'), null, 'no HTTP client, no call');
++
++// Jobs.
++assert.deepStrictEqual(X.extractJobs([
++ "jobs.every('restream-viewer-counts', 60_000, poll);",
++ 'setInterval(sweep, 5 * 60 * 1000);',
++].join('\n')).map(j => `${j.kind}:${j.name}:${j.intervalMs}`), ['jobs.every:restream-viewer-counts:60000', 'setInterval:null:300000']);
++
++console.log('roadmap-baseline extractors: ok');