openvibers/openvibe.host Commits
Host: the example inventory lists every service on the host (14 added with their drills; Sites removed) (#101)
Changes
@@@ -856,42 +856,6 @@"policy": "report"}},-- "sites": {-- "metrics": false,-- "repo": "/opt/openvibe.sites",-- "strategy": "static-build",-- "_strategyNote": "Sites deploy/scripts/deploy.sh as ovhost deploy sites: the pull always comes first (ovhost fetches and fast-forwards before building, so a stale checkout never installs old vhosts); the tracked dist/ a previous build rewrote (dates) is restored from git before the merge (generated); npm ci; node build.js; every deploy/nginx/*.conf installed and enabled, nginx -t, reload, and on a failed nginx -t the previous vhost files come back and nginx is not reloaded; then one release notification per dist/<domain>/release.json. A vhost that left the repository is named, never removed.",-- "generated": [-- "dist/"-- ],-- "units": [],-- "install": {-- "command": [-- "npm",-- "ci",-- "--omit=dev",-- "--no-audit",-- "--no-fund"-- ],-- "always": true-- },-- "build": [-- [-- "node",-- "build.js"-- ]-- ],-- "nginx": {-- "repoVhosts": "deploy/nginx/*.conf",-- "installOnDeploy": true,-- "skipVhosts": [-- "openvibe.bot.conf"-- ]-- },-- "announce": {-- "releaseFiles": "dist/*/release.json"-- }-- },"host": {"_note": "OpenVibe.Host hosting comparison catalog on port 4910; PostgreSQL database ov_host.","repo": "/opt/openvibe.host",@@@ -2203,6 +2167,1013 @@"drain": {"policy": "refuse"}++ },++ "actor": {++ "_note": "Added 2026-10-08 (plan T17): OpenVibe.Actor, the task router, at openvibe.actor on loopback :4950.",++ "repo": "/opt/openvibe.actor",++ "units": [++ "openvibe-actor.service"++ ],++ "unitSources": {++ "openvibe-actor.service": "deploy/systemd/openvibe-actor.service"++ },++ "envFile": "/etc/openvibe/actor.env",++ "env": {++ "required": "from-example"++ },++ "port": 4950,++ "ready": {++ "url": "http://127.0.0.1:4950/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "actor",++ "engine": "postgresql",++ "database": "ov_actor"++ }++ ],++ "drill": {++ "port": 14950,++ "databases": {++ "actor": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "VALKEY_URL": "redis://127.0.0.1:9/0",++ "OV_OAUTH_CLIENT_SECRET": "",++ "ACTOR_EVENTS_URL": "",++ "ACTOR_EVENTS_SECRET": "",++ "ACTOR_DEEPSEEK_API_KEY": "",++ "ACTOR_OPENAI_API_KEY": "",++ "ACTOR_LOCAL_LLM_URL": "http://127.0.0.1:9/v1",++ "ACTOR_SEARCH_URL": "http://127.0.0.1:9",++ "ACTOR_TOOLS_URL": "http://127.0.0.1:9",++ "ACTOR_MAX_RUNNING": "0",++ "ACTOR_AGENTS_TICK_MAX": "0",++ "ACTOR_WEBHOOK_INTERVAL_MS": "86400000"++ },++ "acceptance": [++ {++ "path": "/api/ready",++ "status": 200,++ "keys": []++ }++ ],++ "counts": [++ {++ "db": "actor",++ "table": "tasks"++ },++ {++ "db": "actor",++ "table": "actor_agents"++ },++ {++ "db": "actor",++ "table": "actor_approvals"++ }++ ],++ "countsTolerance": 20,++ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness. No model keys and no running slots, so a queued task is never resumed against a provider. Readiness needs one agent system: the local model URL is a closed port."++ }++ },++ "food": {++ "_note": "Added 2026-10-08 (plan T19): OpenVibe.Food, food near you, meal plans and a pantry, at openvibe.food on loopback :4970.",++ "repo": "/opt/openvibe.food",++ "units": [++ "openvibe-food.service"++ ],++ "unitSources": {++ "openvibe-food.service": "deploy/systemd/openvibe-food.service"++ },++ "envFile": "/etc/openvibe/food.env",++ "env": {++ "required": "from-example"++ },++ "port": 4970,++ "ready": {++ "url": "http://127.0.0.1:4970/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "food",++ "engine": "postgresql",++ "database": "ov_food"++ }++ ],++ "drill": {++ "port": 14960,++ "databases": {++ "food": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "VALKEY_URL": "redis://127.0.0.1:9/0",++ "OV_OAUTH_CLIENT_SECRET": "",++ "FOOD_EVENTS_URL": "",++ "FOOD_EVENTS_SECRET": "",++ "OV_AI_INTERNAL_URL": "",++ "FOOD_NOMINATIM_URL": "http://127.0.0.1:9",++ "FOOD_OVERPASS_URL": "http://127.0.0.1:9"++ },++ "acceptance": [++ {++ "path": "/api/ready",++ "status": 200,++ "keys": []++ }++ ],++ "counts": [++ {++ "db": "food",++ "table": "food_plans"++ },++ {++ "db": "food",++ "table": "food_pantry"++ }++ ],++ "countsTolerance": 5,++ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."++ }++ },++ "help": {++ "_note": "Added 2026-10-08 (plan T19): OpenVibe.Help, the help centre and support tickets, at openvibe.help on loopback :5020.",++ "repo": "/opt/openvibe.help",++ "units": [++ "openvibe-help.service"++ ],++ "unitSources": {++ "openvibe-help.service": "deploy/systemd/openvibe-help.service"++ },++ "envFile": "/etc/openvibe/help.env",++ "env": {++ "required": "from-example"++ },++ "port": 5020,++ "ready": {++ "url": "http://127.0.0.1:5020/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "help",++ "engine": "postgresql",++ "database": "ov_help"++ }++ ],++ "drill": {++ "port": 14970,++ "databases": {++ "help": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "VALKEY_URL": "redis://127.0.0.1:9/0",++ "OV_OAUTH_CLIENT_SECRET": "",++ "HELP_EVENTS_URL": "",++ "HELP_EVENTS_SECRET": "",++ "HELP_SEARCH_URL": "http://127.0.0.1:9"++ },++ "acceptance": [++ {++ "path": "/api/ready",++ "status": 200,++ "keys": []++ }++ ],++ "counts": [++ {++ "db": "help",++ "table": "help_tickets"++ },++ {++ "db": "help",++ "table": "help_messages"++ }++ ],++ "countsTolerance": 5,++ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."++ }++ },++ "work": {++ "_note": "Added 2026-10-08 (plan T19): OpenVibe.Work, job listings from open boards and saved searches, at openvibe.work on loopback :4960.",++ "repo": "/opt/openvibe.work",++ "units": [++ "openvibe-work.service"++ ],++ "unitSources": {++ "openvibe-work.service": "deploy/systemd/openvibe-work.service"++ },++ "envFile": "/etc/openvibe/work.env",++ "env": {++ "required": "from-example"++ },++ "port": 4960,++ "ready": {++ "url": "http://127.0.0.1:4960/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "work",++ "engine": "postgresql",++ "database": "ov_work"++ }++ ],++ "drill": {++ "port": 14980,++ "databases": {++ "work": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "VALKEY_URL": "redis://127.0.0.1:9/0",++ "OV_OAUTH_CLIENT_SECRET": "",++ "WORK_EVENTS_URL": "",++ "WORK_EVENTS_SECRET": "",++ "WORK_INGEST": "off",++ "WORK_ACTOR_URL": "http://127.0.0.1:9"++ },++ "acceptance": [++ {++ "path": "/api/ready",++ "status": 200,++ "keys": []++ }++ ],++ "counts": [++ {++ "db": "work",++ "table": "work_listings"++ },++ {++ "db": "work",++ "table": "work_saved_searches"++ },++ {++ "db": "work",++ "table": "work_applications"++ }++ ],++ "countsTolerance": 400,++ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness. work_listings moves with every six-hourly ingest (new and expired listings): tolerance 400."++ }++ },++ "quest": {++ "_note": "Added 2026-10-08 (plan T19): OpenVibe.Quest, the shared quest log, at openvibe.quest on loopback :4980.",++ "repo": "/opt/openvibe.quest",++ "units": [++ "openvibe-quest.service"++ ],++ "unitSources": {++ "openvibe-quest.service": "deploy/systemd/openvibe-quest.service"++ },++ "envFile": "/etc/openvibe/quest.env",++ "env": {++ "required": "from-example"++ },++ "port": 4980,++ "ready": {++ "url": "http://127.0.0.1:4980/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "quest",++ "engine": "postgresql",++ "database": "ov_quest"++ }++ ],++ "drill": {++ "port": 14990,++ "databases": {++ "quest": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "VALKEY_URL": "redis://127.0.0.1:9/0",++ "OV_OAUTH_CLIENT_SECRET": "",++ "QUEST_EVENTS_URL": "",++ "QUEST_EVENTS_SECRET": "",++ "QUEST_COINS": "off",++ "QUEST_ITEMS": "off",++ "OV_INVENTORY_INTERNAL_URL": "http://127.0.0.1:9"++ },++ "acceptance": [++ {++ "path": "/api/ready",++ "status": 200,++ "keys": []++ }++ ],++ "counts": [++ {++ "db": "quest",++ "table": "quest_progress"++ },++ {++ "db": "quest",++ "table": "quest_completions"++ },++ {++ "db": "quest",++ "table": "quest_badges"++ }++ ],++ "countsTolerance": 20,++ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."++ }++ },++ "rent": {++ "_note": "Added 2026-10-08 (plan T19): OpenVibe.Rent, listings people post, at openvibe.rent on loopback :5010.",++ "repo": "/opt/openvibe.rent",++ "units": [++ "openvibe-rent.service"++ ],++ "unitSources": {++ "openvibe-rent.service": "deploy/systemd/openvibe-rent.service"++ },++ "envFile": "/etc/openvibe/rent.env",++ "env": {++ "required": "from-example"++ },++ "port": 5010,++ "ready": {++ "url": "http://127.0.0.1:5010/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "rent",++ "engine": "postgresql",++ "database": "ov_rent"++ }++ ],++ "drill": {++ "port": 15000,++ "databases": {++ "rent": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "VALKEY_URL": "redis://127.0.0.1:9/0",++ "OV_OAUTH_CLIENT_SECRET": "",++ "RENT_EVENTS_URL": "",++ "RENT_EVENTS_SECRET": ""++ },++ "acceptance": [++ {++ "path": "/api/ready",++ "status": 200,++ "keys": []++ }++ ],++ "counts": [++ {++ "db": "rent",++ "table": "rent_listings"++ },++ {++ "db": "rent",++ "table": "rent_saved_searches"++ },++ {++ "db": "rent",++ "table": "area_price_index"++ }++ ],++ "countsTolerance": 5,++ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."++ }++ },++ "media-hub": {++ "_note": "Added 2026-10-08 (plan T10/T19): OpenVibe.MediaHub, openvibe.download as a private drive (pics and video coming), loopback :4990.",++ "repo": "/opt/openvibe.download",++ "units": [++ "openvibe-media-hub.service"++ ],++ "unitSources": {++ "openvibe-media-hub.service": "deploy/systemd/openvibe-media-hub.service"++ },++ "envFile": "/etc/openvibe/media-hub.env",++ "env": {++ "required": "from-example"++ },++ "port": 4990,++ "ready": {++ "url": "http://127.0.0.1:4990/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "media-hub",++ "engine": "postgresql",++ "database": "ov_media_hub"++ }++ ],++ "drill": {++ "port": 15010,++ "databases": {++ "media-hub": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "VALKEY_URL": "redis://127.0.0.1:9/0",++ "OV_OAUTH_CLIENT_SECRET": "",++ "MEDIAHUB_EVENTS_URL": "",++ "MEDIAHUB_EVENTS_SECRET": "",++ "MEDIAHUB_MEDIA_URL": "http://127.0.0.1:9",++ "MEDIAHUB_LIVE_URL": "http://127.0.0.1:9",++ "MEDIAHUB_CHAT_URL": "http://127.0.0.1:9",++ "MEDIAHUB_TMP_DIR": "{tmp}/tmp"++ },++ "acceptance": [++ {++ "path": "/api/ready",++ "status": 200,++ "keys": []++ }++ ],++ "counts": [++ {++ "db": "media-hub",++ "table": "mh_files"++ },++ {++ "db": "media-hub",++ "table": "mh_folders"++ },++ {++ "db": "media-hub",++ "table": "mh_shares"++ },++ {++ "db": "media-hub",++ "table": "video_playlists"++ }++ ],++ "countsTolerance": 5,++ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."++ }++ },++ "inventory": {++ "_note": "Added 2026-10-09 (plan T21, ADR-054): OpenVibe.Inventory, the network-wide inventory at inventory.openvibe.network, loopback :5030.",++ "repo": "/opt/inventory.openvibe.network",++ "units": [++ "openvibe-inventory.service"++ ],++ "unitSources": {++ "openvibe-inventory.service": "deploy/systemd/openvibe-inventory.service"++ },++ "envFile": "/etc/openvibe/inventory.env",++ "env": {++ "required": "from-example"++ },++ "port": 5030,++ "ready": {++ "url": "http://127.0.0.1:5030/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "inventory",++ "engine": "postgresql",++ "database": "ov_inventory"++ }++ ],++ "drill": {++ "port": 15020,++ "databases": {++ "inventory": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "VALKEY_URL": "redis://127.0.0.1:9/0",++ "OV_OAUTH_CLIENT_SECRET": "",++ "EVENTS_URL": "",++ "INVENTORY_EVENTS_SECRET": "",++ "OV_MEDIA_INTERNAL_URL": "http://127.0.0.1:9"++ },++ "acceptance": [++ {++ "path": "/api/ready",++ "status": 200,++ "keys": []++ }++ ],++ "counts": [++ {++ "db": "inventory",++ "table": "inventory_definitions"++ },++ {++ "db": "inventory",++ "table": "inventory_instances"++ },++ {++ "db": "inventory",++ "table": "inventory_ledger"++ }++ ],++ "countsTolerance": 50,++ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."++ }++ },++ "play": {++ "_note": "Added 2026-10-14 (Games ADR-0007 decision 10, contracts 0.172.0): OpenVibe.Play, achievements and leaderboards at play.openvibe.network, loopback :5040.",++ "repo": "/opt/play.openvibe.network",++ "units": [++ "openvibe-play.service"++ ],++ "unitSources": {++ "openvibe-play.service": "deploy/systemd/openvibe-play.service"++ },++ "envFile": "/etc/openvibe/play.env",++ "env": {++ "required": "from-example"++ },++ "port": 5040,++ "ready": {++ "url": "http://127.0.0.1:5040/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "play",++ "engine": "postgresql",++ "database": "ov_play"++ }++ ],++ "drill": {++ "port": 15030,++ "databases": {++ "play": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "VALKEY_URL": "redis://127.0.0.1:9/0",++ "OV_OAUTH_CLIENT_SECRET": "",++ "EVENTS_URL": "",++ "PLAY_EVENTS_SECRET": ""++ },++ "acceptance": [++ {++ "path": "/api/ready",++ "status": 200,++ "keys": []++ }++ ],++ "counts": [++ {++ "db": "play",++ "table": "play_titles"++ },++ {++ "db": "play",++ "table": "play_achievements"++ },++ {++ "db": "play",++ "table": "play_unlocks"++ },++ {++ "db": "play",++ "table": "play_scores"++ }++ ],++ "countsTolerance": 50,++ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."++ }++ },++ "zone": {++ "_note": "Added 2026-10-14 (ADR-031 amendment, contracts 0.173.0): OpenVibe.Zone, private S3-compatible storage zones at openvibe.zone, loopback :5050. Media serves the bytes (s3.openvibe.media / s3.openvibe.network).",++ "repo": "/opt/openvibe.zone",++ "units": [++ "openvibe-zone.service"++ ],++ "unitSources": {++ "openvibe-zone.service": "deploy/systemd/openvibe-zone.service"++ },++ "envFile": "/etc/openvibe/zone.env",++ "env": {++ "required": "from-example"++ },++ "port": 5050,++ "ready": {++ "url": "http://127.0.0.1:5050/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "zone",++ "engine": "postgresql",++ "database": "ov_zone"++ }++ ],++ "drill": {++ "port": 15040,++ "databases": {++ "zone": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "VALKEY_URL": "redis://127.0.0.1:9/0",++ "OV_OAUTH_CLIENT_SECRET": "",++ "EVENTS_URL": "",++ "ZONE_EVENTS_SECRET": "",++ "MEDIA_INTERNAL_URL": "http://127.0.0.1:9"++ },++ "acceptance": [++ {++ "path": "/api/ready",++ "status": 200,++ "keys": []++ }++ ],++ "counts": [++ {++ "db": "zone",++ "table": "zone_zones"++ },++ {++ "db": "zone",++ "table": "zone_usage"++ }++ ],++ "countsTolerance": 5,++ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."++ }++ },++ "watch": {++ "_note": "Added 2026-10-07 (plan T18): OpenVibe.Watch on loopback :4730; no public vhost until its product surface ships.",++ "repo": "/opt/openvibe.watch",++ "units": [++ "openvibe-watch.service"++ ],++ "unitSources": {++ "openvibe-watch.service": "deploy/systemd/openvibe-watch.service"++ },++ "envFile": "/etc/openvibe/watch.env",++ "env": {++ "required": "from-example"++ },++ "port": 4730,++ "ready": {++ "url": "http://127.0.0.1:4730/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "watch",++ "engine": "postgresql",++ "database": "ov_watch"++ }++ ],++ "drill": {++ "port": 14730,++ "databases": {++ "watch": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "WATCH_WORKER": "off",++ "EVENTS_URL": "",++ "OV_OAUTH_CLIENT_SECRET": "",++ "VALKEY_URL": "redis://127.0.0.1:9/0"++ },++ "counts": [++ {++ "db": "watch",++ "table": "watches"++ },++ {++ "db": "watch",++ "table": "observations"++ },++ {++ "db": "watch",++ "table": "check_runs"++ }++ ],++ "acceptance": [++ {++ "path": "/api/ready",++ "keys": [++ "ready",++ "checks"++ ]++ },++ {++ "path": "/api/health"++ }++ ]++ }++ },++ "run": {++ "_note": "Added 2026-10-07 (plan T14): OpenVibe.Run on loopback :4920; jobs dispatch through Bot's internal jobs API.",++ "repo": "/opt/openvibe.run",++ "units": [++ "openvibe-run.service"++ ],++ "unitSources": {++ "openvibe-run.service": "deploy/systemd/openvibe-run.service"++ },++ "envFile": "/etc/openvibe/run.env",++ "env": {++ "required": "from-example"++ },++ "port": 4920,++ "ready": {++ "url": "http://127.0.0.1:4920/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "run",++ "engine": "postgresql",++ "database": "ov_run"++ }++ ],++ "drill": {++ "port": 14920,++ "databases": {++ "run": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "RUN_POLL_MS": "0",++ "EVENTS_URL": "",++ "OV_OAUTH_CLIENT_SECRET": "",++ "VALKEY_URL": "redis://127.0.0.1:9/0"++ },++ "counts": [++ {++ "db": "run",++ "table": "run_jobs"++ }++ ],++ "acceptance": [++ {++ "path": "/api/ready",++ "keys": [++ "ready",++ "checks"++ ]++ },++ {++ "path": "/api/health"++ }++ ]++ }++ },++ "services": {++ "_note": "Added 2026-10-07 (plan T13): OpenVibe.Services on loopback :4930.",++ "repo": "/opt/openvibe.services",++ "units": [++ "openvibe-services.service"++ ],++ "unitSources": {++ "openvibe-services.service": "deploy/systemd/openvibe-services.service"++ },++ "envFile": "/etc/openvibe/services.env",++ "env": {++ "required": "from-example"++ },++ "port": 4930,++ "ready": {++ "url": "http://127.0.0.1:4930/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "services",++ "engine": "postgresql",++ "database": "ov_services"++ }++ ],++ "drill": {++ "port": 14930,++ "databases": {++ "services": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "EVENTS_URL": "",++ "OV_OAUTH_CLIENT_SECRET": "",++ "VALKEY_URL": "redis://127.0.0.1:9/0"++ },++ "counts": [],++ "acceptance": [++ {++ "path": "/api/ready",++ "keys": [++ "ready",++ "checks"++ ]++ },++ {++ "path": "/api/health"++ }++ ]++ }++ },++ "space": {++ "_note": "Added 2026-10-07 (plan T10 step 3): OpenVibe.Space, the forum moved off OpenVibe.Community; loopback :4940 behind openvibe.space.",++ "repo": "/opt/openvibe.space",++ "units": [++ "openvibe-space.service"++ ],++ "unitSources": {++ "openvibe-space.service": "deploy/systemd/openvibe-space.service"++ },++ "envFile": "/etc/openvibe/space.env",++ "env": {++ "required": "from-example"++ },++ "port": 4940,++ "ready": {++ "url": "http://127.0.0.1:4940/api/ready",++ "timeoutSeconds": 30++ },++ "noRestartPaths": [++ "docs/",++ "test/",++ "**/*.md"++ ],++ "databases": [++ {++ "name": "space",++ "engine": "postgresql",++ "database": "ov_space"++ }++ ],++ "drill": {++ "port": 14940,++ "databases": {++ "space": {++ "url": "DATABASE_URL",++ "directUrl": "DATABASE_DIRECT_URL"++ }++ },++ "env": {++ "PORT": "{port}",++ "HOST": "127.0.0.1",++ "EVENTS_URL": "",++ "OV_OAUTH_CLIENT_SECRET": "",++ "VALKEY_URL": "redis://127.0.0.1:9/0"++ },++ "counts": [++ {++ "db": "space",++ "table": "spaces"++ },++ {++ "db": "space",++ "table": "threads"++ },++ {++ "db": "space",++ "table": "posts"++ }++ ],++ "acceptance": [++ {++ "path": "/api/ready",++ "keys": [++ "ready",++ "checks"++ ]++ },++ {++ "path": "/api/health"++ }++ ]++ },++ "nginx": {++ "vhost": "openvibe.space.conf",++ "repoVhost": "deploy/nginx/openvibe.space.conf",++ "variant": "http"++ }}}}@@@ -17,7 +17,10 @@ runTests([test('host.example.json is a valid inventory for every service on the host', () => {const raw = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'host.example.json'), 'utf8'));const inv = normalise(raw);-- assert.deepStrictEqual(Object.keys(inv.services), ['network', 'live', 'media', 'tools', 'community', 'events', 'games', 'sites', 'host', 'billing', 'chat', 'search', 'sources', 'wiki', 'blog', 'tips', 'vip', 'news', 'reviews', 'deals', 'coupons', 'trade', 'codes', 'ai', 'bot', 'openre']);++ // Every service on the host (2026-10-15): Sites retired, and the fourteen added since the first copy of this file.++ assert.deepStrictEqual(Object.keys(inv.services), ['network', 'live', 'media', 'tools', 'community', 'events', 'games', 'host', 'billing', 'chat', 'search', 'sources', 'wiki', 'blog', 'tips', 'vip', 'news', 'reviews', 'deals', 'coupons', 'trade', 'codes', 'ai', 'bot', 'openre',++ 'actor', 'food', 'help', 'work', 'quest', 'rent', 'media-hub', 'inventory', 'play', 'zone', 'watch', 'run', 'services', 'space']);++ assert.ok(Object.values(inv.services).filter((s) => s.databases.length).every((s) => s.drill), 'every service with a database has a restore drill');assert.strictEqual(inv.services.live.socketUnit, 'openvibe-live.socket');assert.ok(!inv.services.live.units.includes('openvibe-live.socket'));assert.strictEqual(inv.services.live.protected.url, 'http://127.0.0.1:3000/api/streams');@@@ -25,7 +28,7 @@ runTests([assert.deepStrictEqual([inv.services.media.protected.kind, inv.services.media.protected.field], ['http-json-count', 'recordings_in_progress']);// Every service on the host is deployed by a strategy (WS-N task 11): Games by pnpm-build.assert.ok(Object.values(inv.services).every((s) => s.managed), 'every service is managed');-- assert.deepStrictEqual(Object.fromEntries(['live', 'tools', 'sites', 'games', 'openre', 'network'].map((id) => [id, inv.services[id].strategy])), { live: 'release-layout', tools: 'multi-app', sites: 'static-build', games: 'pnpm-build', openre: 'release-layout', network: 'git-checkout' });++ assert.deepStrictEqual(Object.fromEntries(['live', 'tools', 'games', 'openre', 'network'].map((id) => [id, inv.services[id].strategy])), { live: 'release-layout', tools: 'multi-app', games: 'pnpm-build', openre: 'release-layout', network: 'git-checkout' });// Live's and OpenRestream's release layouts are root-owned clones (git as root); their services and drills run as ubuntu.for (const s of Object.values(inv.services)) assert.strictEqual(s.owner, ['live', 'openre'].includes(s.id) ? 'root' : 'ubuntu', `${s.id} checkout owner`);assert.strictEqual(inv.services.live.runAs, 'ubuntu');@@@ -34,7 +37,6 @@ runTests([assert.strictEqual(inv.services.live.codeDir, '/opt/openvibe.live/current');assert.strictEqual(inv.services.live.release.git, '/opt/openvibe.live/repo');assert.strictEqual(inv.services.openre.release.git, '/opt/openre.stream/repo');-- assert.deepStrictEqual(inv.services.sites.nginx.skipVhosts, ['openvibe.bot.conf']);assert.deepStrictEqual([inv.services.bot.nginx.vhost, inv.services.bot.nginx.repoVhost, inv.services.bot.nginx.installOnDeploy], ['openvibe.bot.conf', 'deploy/nginx/openvibe.bot.conf', true]);assert.strictEqual(inv.services.bot.installUnits, true);assert.strictEqual(inv.services.bot.enableUnits, true);
@@ -856,42 +856,6 @@
@@ -856,42 +856,6 @@
"policy": "report"
"policy": "report"
}
}
},
},
- "sites": {
- "metrics": false,
- "repo": "/opt/openvibe.sites",
- "strategy": "static-build",
- "_strategyNote": "Sites deploy/scripts/deploy.sh as ovhost deploy sites: the pull always comes first (ovhost fetches and fast-forwards before building, so a stale checkout never installs old vhosts); the tracked dist/ a previous build rewrote (dates) is restored from git before the merge (generated); npm ci; node build.js; every deploy/nginx/*.conf installed and enabled, nginx -t, reload, and on a failed nginx -t the previous vhost files come back and nginx is not reloaded; then one release notification per dist/<domain>/release.json. A vhost that left the repository is named, never removed.",
- "generated": [
- "dist/"
- ],
- "units": [],
- "install": {
- "command": [
- "npm",
- "ci",
- "--omit=dev",
- "--no-audit",
- "--no-fund"
- ],
- "always": true
- },
- "build": [
- [
- "node",
- "build.js"
- ]
- ],
- "nginx": {
- "repoVhosts": "deploy/nginx/*.conf",
- "installOnDeploy": true,
- "skipVhosts": [
- "openvibe.bot.conf"
- ]
- },
- "announce": {
- "releaseFiles": "dist/*/release.json"
- }
- },
"host": {
"host": {
"_note": "OpenVibe.Host hosting comparison catalog on port 4910; PostgreSQL database ov_host.",
"_note": "OpenVibe.Host hosting comparison catalog on port 4910; PostgreSQL database ov_host.",
"repo": "/opt/openvibe.host",
"repo": "/opt/openvibe.host",
@@ -2203,6 +2167,1013 @@
@@ -2203,6 +2167,1013 @@
"drain": {
"drain": {
"policy": "refuse"
"policy": "refuse"
}
}
+ },
+ "actor": {
+ "_note": "Added 2026-10-08 (plan T17): OpenVibe.Actor, the task router, at openvibe.actor on loopback :4950.",
+ "repo": "/opt/openvibe.actor",
+ "units": [
+ "openvibe-actor.service"
+ ],
+ "unitSources": {
+ "openvibe-actor.service": "deploy/systemd/openvibe-actor.service"
+ },
+ "envFile": "/etc/openvibe/actor.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 4950,
+ "ready": {
+ "url": "http://127.0.0.1:4950/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "actor",
+ "engine": "postgresql",
+ "database": "ov_actor"
+ }
+ ],
+ "drill": {
+ "port": 14950,
+ "databases": {
+ "actor": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "VALKEY_URL": "redis://127.0.0.1:9/0",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "ACTOR_EVENTS_URL": "",
+ "ACTOR_EVENTS_SECRET": "",
+ "ACTOR_DEEPSEEK_API_KEY": "",
+ "ACTOR_OPENAI_API_KEY": "",
+ "ACTOR_LOCAL_LLM_URL": "http://127.0.0.1:9/v1",
+ "ACTOR_SEARCH_URL": "http://127.0.0.1:9",
+ "ACTOR_TOOLS_URL": "http://127.0.0.1:9",
+ "ACTOR_MAX_RUNNING": "0",
+ "ACTOR_AGENTS_TICK_MAX": "0",
+ "ACTOR_WEBHOOK_INTERVAL_MS": "86400000"
+ },
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "status": 200,
+ "keys": []
+ }
+ ],
+ "counts": [
+ {
+ "db": "actor",
+ "table": "tasks"
+ },
+ {
+ "db": "actor",
+ "table": "actor_agents"
+ },
+ {
+ "db": "actor",
+ "table": "actor_approvals"
+ }
+ ],
+ "countsTolerance": 20,
+ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness. No model keys and no running slots, so a queued task is never resumed against a provider. Readiness needs one agent system: the local model URL is a closed port."
+ }
+ },
+ "food": {
+ "_note": "Added 2026-10-08 (plan T19): OpenVibe.Food, food near you, meal plans and a pantry, at openvibe.food on loopback :4970.",
+ "repo": "/opt/openvibe.food",
+ "units": [
+ "openvibe-food.service"
+ ],
+ "unitSources": {
+ "openvibe-food.service": "deploy/systemd/openvibe-food.service"
+ },
+ "envFile": "/etc/openvibe/food.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 4970,
+ "ready": {
+ "url": "http://127.0.0.1:4970/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "food",
+ "engine": "postgresql",
+ "database": "ov_food"
+ }
+ ],
+ "drill": {
+ "port": 14960,
+ "databases": {
+ "food": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "VALKEY_URL": "redis://127.0.0.1:9/0",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "FOOD_EVENTS_URL": "",
+ "FOOD_EVENTS_SECRET": "",
+ "OV_AI_INTERNAL_URL": "",
+ "FOOD_NOMINATIM_URL": "http://127.0.0.1:9",
+ "FOOD_OVERPASS_URL": "http://127.0.0.1:9"
+ },
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "status": 200,
+ "keys": []
+ }
+ ],
+ "counts": [
+ {
+ "db": "food",
+ "table": "food_plans"
+ },
+ {
+ "db": "food",
+ "table": "food_pantry"
+ }
+ ],
+ "countsTolerance": 5,
+ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."
+ }
+ },
+ "help": {
+ "_note": "Added 2026-10-08 (plan T19): OpenVibe.Help, the help centre and support tickets, at openvibe.help on loopback :5020.",
+ "repo": "/opt/openvibe.help",
+ "units": [
+ "openvibe-help.service"
+ ],
+ "unitSources": {
+ "openvibe-help.service": "deploy/systemd/openvibe-help.service"
+ },
+ "envFile": "/etc/openvibe/help.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 5020,
+ "ready": {
+ "url": "http://127.0.0.1:5020/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "help",
+ "engine": "postgresql",
+ "database": "ov_help"
+ }
+ ],
+ "drill": {
+ "port": 14970,
+ "databases": {
+ "help": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "VALKEY_URL": "redis://127.0.0.1:9/0",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "HELP_EVENTS_URL": "",
+ "HELP_EVENTS_SECRET": "",
+ "HELP_SEARCH_URL": "http://127.0.0.1:9"
+ },
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "status": 200,
+ "keys": []
+ }
+ ],
+ "counts": [
+ {
+ "db": "help",
+ "table": "help_tickets"
+ },
+ {
+ "db": "help",
+ "table": "help_messages"
+ }
+ ],
+ "countsTolerance": 5,
+ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."
+ }
+ },
+ "work": {
+ "_note": "Added 2026-10-08 (plan T19): OpenVibe.Work, job listings from open boards and saved searches, at openvibe.work on loopback :4960.",
+ "repo": "/opt/openvibe.work",
+ "units": [
+ "openvibe-work.service"
+ ],
+ "unitSources": {
+ "openvibe-work.service": "deploy/systemd/openvibe-work.service"
+ },
+ "envFile": "/etc/openvibe/work.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 4960,
+ "ready": {
+ "url": "http://127.0.0.1:4960/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "work",
+ "engine": "postgresql",
+ "database": "ov_work"
+ }
+ ],
+ "drill": {
+ "port": 14980,
+ "databases": {
+ "work": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "VALKEY_URL": "redis://127.0.0.1:9/0",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "WORK_EVENTS_URL": "",
+ "WORK_EVENTS_SECRET": "",
+ "WORK_INGEST": "off",
+ "WORK_ACTOR_URL": "http://127.0.0.1:9"
+ },
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "status": 200,
+ "keys": []
+ }
+ ],
+ "counts": [
+ {
+ "db": "work",
+ "table": "work_listings"
+ },
+ {
+ "db": "work",
+ "table": "work_saved_searches"
+ },
+ {
+ "db": "work",
+ "table": "work_applications"
+ }
+ ],
+ "countsTolerance": 400,
+ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness. work_listings moves with every six-hourly ingest (new and expired listings): tolerance 400."
+ }
+ },
+ "quest": {
+ "_note": "Added 2026-10-08 (plan T19): OpenVibe.Quest, the shared quest log, at openvibe.quest on loopback :4980.",
+ "repo": "/opt/openvibe.quest",
+ "units": [
+ "openvibe-quest.service"
+ ],
+ "unitSources": {
+ "openvibe-quest.service": "deploy/systemd/openvibe-quest.service"
+ },
+ "envFile": "/etc/openvibe/quest.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 4980,
+ "ready": {
+ "url": "http://127.0.0.1:4980/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "quest",
+ "engine": "postgresql",
+ "database": "ov_quest"
+ }
+ ],
+ "drill": {
+ "port": 14990,
+ "databases": {
+ "quest": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "VALKEY_URL": "redis://127.0.0.1:9/0",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "QUEST_EVENTS_URL": "",
+ "QUEST_EVENTS_SECRET": "",
+ "QUEST_COINS": "off",
+ "QUEST_ITEMS": "off",
+ "OV_INVENTORY_INTERNAL_URL": "http://127.0.0.1:9"
+ },
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "status": 200,
+ "keys": []
+ }
+ ],
+ "counts": [
+ {
+ "db": "quest",
+ "table": "quest_progress"
+ },
+ {
+ "db": "quest",
+ "table": "quest_completions"
+ },
+ {
+ "db": "quest",
+ "table": "quest_badges"
+ }
+ ],
+ "countsTolerance": 20,
+ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."
+ }
+ },
+ "rent": {
+ "_note": "Added 2026-10-08 (plan T19): OpenVibe.Rent, listings people post, at openvibe.rent on loopback :5010.",
+ "repo": "/opt/openvibe.rent",
+ "units": [
+ "openvibe-rent.service"
+ ],
+ "unitSources": {
+ "openvibe-rent.service": "deploy/systemd/openvibe-rent.service"
+ },
+ "envFile": "/etc/openvibe/rent.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 5010,
+ "ready": {
+ "url": "http://127.0.0.1:5010/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "rent",
+ "engine": "postgresql",
+ "database": "ov_rent"
+ }
+ ],
+ "drill": {
+ "port": 15000,
+ "databases": {
+ "rent": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "VALKEY_URL": "redis://127.0.0.1:9/0",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "RENT_EVENTS_URL": "",
+ "RENT_EVENTS_SECRET": ""
+ },
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "status": 200,
+ "keys": []
+ }
+ ],
+ "counts": [
+ {
+ "db": "rent",
+ "table": "rent_listings"
+ },
+ {
+ "db": "rent",
+ "table": "rent_saved_searches"
+ },
+ {
+ "db": "rent",
+ "table": "area_price_index"
+ }
+ ],
+ "countsTolerance": 5,
+ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."
+ }
+ },
+ "media-hub": {
+ "_note": "Added 2026-10-08 (plan T10/T19): OpenVibe.MediaHub, openvibe.download as a private drive (pics and video coming), loopback :4990.",
+ "repo": "/opt/openvibe.download",
+ "units": [
+ "openvibe-media-hub.service"
+ ],
+ "unitSources": {
+ "openvibe-media-hub.service": "deploy/systemd/openvibe-media-hub.service"
+ },
+ "envFile": "/etc/openvibe/media-hub.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 4990,
+ "ready": {
+ "url": "http://127.0.0.1:4990/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "media-hub",
+ "engine": "postgresql",
+ "database": "ov_media_hub"
+ }
+ ],
+ "drill": {
+ "port": 15010,
+ "databases": {
+ "media-hub": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "VALKEY_URL": "redis://127.0.0.1:9/0",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "MEDIAHUB_EVENTS_URL": "",
+ "MEDIAHUB_EVENTS_SECRET": "",
+ "MEDIAHUB_MEDIA_URL": "http://127.0.0.1:9",
+ "MEDIAHUB_LIVE_URL": "http://127.0.0.1:9",
+ "MEDIAHUB_CHAT_URL": "http://127.0.0.1:9",
+ "MEDIAHUB_TMP_DIR": "{tmp}/tmp"
+ },
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "status": 200,
+ "keys": []
+ }
+ ],
+ "counts": [
+ {
+ "db": "media-hub",
+ "table": "mh_files"
+ },
+ {
+ "db": "media-hub",
+ "table": "mh_folders"
+ },
+ {
+ "db": "media-hub",
+ "table": "mh_shares"
+ },
+ {
+ "db": "media-hub",
+ "table": "video_playlists"
+ }
+ ],
+ "countsTolerance": 5,
+ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."
+ }
+ },
+ "inventory": {
+ "_note": "Added 2026-10-09 (plan T21, ADR-054): OpenVibe.Inventory, the network-wide inventory at inventory.openvibe.network, loopback :5030.",
+ "repo": "/opt/inventory.openvibe.network",
+ "units": [
+ "openvibe-inventory.service"
+ ],
+ "unitSources": {
+ "openvibe-inventory.service": "deploy/systemd/openvibe-inventory.service"
+ },
+ "envFile": "/etc/openvibe/inventory.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 5030,
+ "ready": {
+ "url": "http://127.0.0.1:5030/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "inventory",
+ "engine": "postgresql",
+ "database": "ov_inventory"
+ }
+ ],
+ "drill": {
+ "port": 15020,
+ "databases": {
+ "inventory": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "VALKEY_URL": "redis://127.0.0.1:9/0",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "EVENTS_URL": "",
+ "INVENTORY_EVENTS_SECRET": "",
+ "OV_MEDIA_INTERNAL_URL": "http://127.0.0.1:9"
+ },
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "status": 200,
+ "keys": []
+ }
+ ],
+ "counts": [
+ {
+ "db": "inventory",
+ "table": "inventory_definitions"
+ },
+ {
+ "db": "inventory",
+ "table": "inventory_instances"
+ },
+ {
+ "db": "inventory",
+ "table": "inventory_ledger"
+ }
+ ],
+ "countsTolerance": 50,
+ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."
+ }
+ },
+ "play": {
+ "_note": "Added 2026-10-14 (Games ADR-0007 decision 10, contracts 0.172.0): OpenVibe.Play, achievements and leaderboards at play.openvibe.network, loopback :5040.",
+ "repo": "/opt/play.openvibe.network",
+ "units": [
+ "openvibe-play.service"
+ ],
+ "unitSources": {
+ "openvibe-play.service": "deploy/systemd/openvibe-play.service"
+ },
+ "envFile": "/etc/openvibe/play.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 5040,
+ "ready": {
+ "url": "http://127.0.0.1:5040/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "play",
+ "engine": "postgresql",
+ "database": "ov_play"
+ }
+ ],
+ "drill": {
+ "port": 15030,
+ "databases": {
+ "play": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "VALKEY_URL": "redis://127.0.0.1:9/0",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "EVENTS_URL": "",
+ "PLAY_EVENTS_SECRET": ""
+ },
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "status": 200,
+ "keys": []
+ }
+ ],
+ "counts": [
+ {
+ "db": "play",
+ "table": "play_titles"
+ },
+ {
+ "db": "play",
+ "table": "play_achievements"
+ },
+ {
+ "db": "play",
+ "table": "play_unlocks"
+ },
+ {
+ "db": "play",
+ "table": "play_scores"
+ }
+ ],
+ "countsTolerance": 50,
+ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."
+ }
+ },
+ "zone": {
+ "_note": "Added 2026-10-14 (ADR-031 amendment, contracts 0.173.0): OpenVibe.Zone, private S3-compatible storage zones at openvibe.zone, loopback :5050. Media serves the bytes (s3.openvibe.media / s3.openvibe.network).",
+ "repo": "/opt/openvibe.zone",
+ "units": [
+ "openvibe-zone.service"
+ ],
+ "unitSources": {
+ "openvibe-zone.service": "deploy/systemd/openvibe-zone.service"
+ },
+ "envFile": "/etc/openvibe/zone.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 5050,
+ "ready": {
+ "url": "http://127.0.0.1:5050/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "zone",
+ "engine": "postgresql",
+ "database": "ov_zone"
+ }
+ ],
+ "drill": {
+ "port": 15040,
+ "databases": {
+ "zone": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "VALKEY_URL": "redis://127.0.0.1:9/0",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "EVENTS_URL": "",
+ "ZONE_EVENTS_SECRET": "",
+ "MEDIA_INTERNAL_URL": "http://127.0.0.1:9"
+ },
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "status": 200,
+ "keys": []
+ }
+ ],
+ "counts": [
+ {
+ "db": "zone",
+ "table": "zone_zones"
+ },
+ {
+ "db": "zone",
+ "table": "zone_usage"
+ }
+ ],
+ "countsTolerance": 5,
+ "_notes": "Drill 2026-10-15: the restored copy boots with no service credentials (no tokens, no event relay or subscriptions), Valkey on a closed port and every outbound service URL on a closed port; counts and readiness."
+ }
+ },
+ "watch": {
+ "_note": "Added 2026-10-07 (plan T18): OpenVibe.Watch on loopback :4730; no public vhost until its product surface ships.",
+ "repo": "/opt/openvibe.watch",
+ "units": [
+ "openvibe-watch.service"
+ ],
+ "unitSources": {
+ "openvibe-watch.service": "deploy/systemd/openvibe-watch.service"
+ },
+ "envFile": "/etc/openvibe/watch.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 4730,
+ "ready": {
+ "url": "http://127.0.0.1:4730/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "watch",
+ "engine": "postgresql",
+ "database": "ov_watch"
+ }
+ ],
+ "drill": {
+ "port": 14730,
+ "databases": {
+ "watch": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "WATCH_WORKER": "off",
+ "EVENTS_URL": "",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "VALKEY_URL": "redis://127.0.0.1:9/0"
+ },
+ "counts": [
+ {
+ "db": "watch",
+ "table": "watches"
+ },
+ {
+ "db": "watch",
+ "table": "observations"
+ },
+ {
+ "db": "watch",
+ "table": "check_runs"
+ }
+ ],
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "keys": [
+ "ready",
+ "checks"
+ ]
+ },
+ {
+ "path": "/api/health"
+ }
+ ]
+ }
+ },
+ "run": {
+ "_note": "Added 2026-10-07 (plan T14): OpenVibe.Run on loopback :4920; jobs dispatch through Bot's internal jobs API.",
+ "repo": "/opt/openvibe.run",
+ "units": [
+ "openvibe-run.service"
+ ],
+ "unitSources": {
+ "openvibe-run.service": "deploy/systemd/openvibe-run.service"
+ },
+ "envFile": "/etc/openvibe/run.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 4920,
+ "ready": {
+ "url": "http://127.0.0.1:4920/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "run",
+ "engine": "postgresql",
+ "database": "ov_run"
+ }
+ ],
+ "drill": {
+ "port": 14920,
+ "databases": {
+ "run": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "RUN_POLL_MS": "0",
+ "EVENTS_URL": "",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "VALKEY_URL": "redis://127.0.0.1:9/0"
+ },
+ "counts": [
+ {
+ "db": "run",
+ "table": "run_jobs"
+ }
+ ],
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "keys": [
+ "ready",
+ "checks"
+ ]
+ },
+ {
+ "path": "/api/health"
+ }
+ ]
+ }
+ },
+ "services": {
+ "_note": "Added 2026-10-07 (plan T13): OpenVibe.Services on loopback :4930.",
+ "repo": "/opt/openvibe.services",
+ "units": [
+ "openvibe-services.service"
+ ],
+ "unitSources": {
+ "openvibe-services.service": "deploy/systemd/openvibe-services.service"
+ },
+ "envFile": "/etc/openvibe/services.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 4930,
+ "ready": {
+ "url": "http://127.0.0.1:4930/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "services",
+ "engine": "postgresql",
+ "database": "ov_services"
+ }
+ ],
+ "drill": {
+ "port": 14930,
+ "databases": {
+ "services": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "EVENTS_URL": "",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "VALKEY_URL": "redis://127.0.0.1:9/0"
+ },
+ "counts": [],
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "keys": [
+ "ready",
+ "checks"
+ ]
+ },
+ {
+ "path": "/api/health"
+ }
+ ]
+ }
+ },
+ "space": {
+ "_note": "Added 2026-10-07 (plan T10 step 3): OpenVibe.Space, the forum moved off OpenVibe.Community; loopback :4940 behind openvibe.space.",
+ "repo": "/opt/openvibe.space",
+ "units": [
+ "openvibe-space.service"
+ ],
+ "unitSources": {
+ "openvibe-space.service": "deploy/systemd/openvibe-space.service"
+ },
+ "envFile": "/etc/openvibe/space.env",
+ "env": {
+ "required": "from-example"
+ },
+ "port": 4940,
+ "ready": {
+ "url": "http://127.0.0.1:4940/api/ready",
+ "timeoutSeconds": 30
+ },
+ "noRestartPaths": [
+ "docs/",
+ "test/",
+ "**/*.md"
+ ],
+ "databases": [
+ {
+ "name": "space",
+ "engine": "postgresql",
+ "database": "ov_space"
+ }
+ ],
+ "drill": {
+ "port": 14940,
+ "databases": {
+ "space": {
+ "url": "DATABASE_URL",
+ "directUrl": "DATABASE_DIRECT_URL"
+ }
+ },
+ "env": {
+ "PORT": "{port}",
+ "HOST": "127.0.0.1",
+ "EVENTS_URL": "",
+ "OV_OAUTH_CLIENT_SECRET": "",
+ "VALKEY_URL": "redis://127.0.0.1:9/0"
+ },
+ "counts": [
+ {
+ "db": "space",
+ "table": "spaces"
+ },
+ {
+ "db": "space",
+ "table": "threads"
+ },
+ {
+ "db": "space",
+ "table": "posts"
+ }
+ ],
+ "acceptance": [
+ {
+ "path": "/api/ready",
+ "keys": [
+ "ready",
+ "checks"
+ ]
+ },
+ {
+ "path": "/api/health"
+ }
+ ]
+ },
+ "nginx": {
+ "vhost": "openvibe.space.conf",
+ "repoVhost": "deploy/nginx/openvibe.space.conf",
+ "variant": "http"
+ }
}
}
}
}
}
}
@@ -17,7 +17,10 @@ runTests([
@@ -17,7 +17,10 @@ runTests([
test('host.example.json is a valid inventory for every service on the host', () => {
test('host.example.json is a valid inventory for every service on the host', () => {
const raw = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'host.example.json'), 'utf8'));
const raw = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'host.example.json'), 'utf8'));
const inv = normalise(raw);
const inv = normalise(raw);
- assert.deepStrictEqual(Object.keys(inv.services), ['network', 'live', 'media', 'tools', 'community', 'events', 'games', 'sites', 'host', 'billing', 'chat', 'search', 'sources', 'wiki', 'blog', 'tips', 'vip', 'news', 'reviews', 'deals', 'coupons', 'trade', 'codes', 'ai', 'bot', 'openre']);
+ // Every service on the host (2026-10-15): Sites retired, and the fourteen added since the first copy of this file.
+ assert.deepStrictEqual(Object.keys(inv.services), ['network', 'live', 'media', 'tools', 'community', 'events', 'games', 'host', 'billing', 'chat', 'search', 'sources', 'wiki', 'blog', 'tips', 'vip', 'news', 'reviews', 'deals', 'coupons', 'trade', 'codes', 'ai', 'bot', 'openre',
+ 'actor', 'food', 'help', 'work', 'quest', 'rent', 'media-hub', 'inventory', 'play', 'zone', 'watch', 'run', 'services', 'space']);
+ assert.ok(Object.values(inv.services).filter((s) => s.databases.length).every((s) => s.drill), 'every service with a database has a restore drill');
assert.strictEqual(inv.services.live.socketUnit, 'openvibe-live.socket');
assert.strictEqual(inv.services.live.socketUnit, 'openvibe-live.socket');
assert.ok(!inv.services.live.units.includes('openvibe-live.socket'));
assert.ok(!inv.services.live.units.includes('openvibe-live.socket'));
assert.strictEqual(inv.services.live.protected.url, 'http://127.0.0.1:3000/api/streams');
assert.strictEqual(inv.services.live.protected.url, 'http://127.0.0.1:3000/api/streams');
@@ -25,7 +28,7 @@ runTests([
@@ -25,7 +28,7 @@ runTests([
assert.deepStrictEqual([inv.services.media.protected.kind, inv.services.media.protected.field], ['http-json-count', 'recordings_in_progress']);
assert.deepStrictEqual([inv.services.media.protected.kind, inv.services.media.protected.field], ['http-json-count', 'recordings_in_progress']);
// Every service on the host is deployed by a strategy (WS-N task 11): Games by pnpm-build.
// Every service on the host is deployed by a strategy (WS-N task 11): Games by pnpm-build.
assert.ok(Object.values(inv.services).every((s) => s.managed), 'every service is managed');
assert.ok(Object.values(inv.services).every((s) => s.managed), 'every service is managed');
- assert.deepStrictEqual(Object.fromEntries(['live', 'tools', 'sites', 'games', 'openre', 'network'].map((id) => [id, inv.services[id].strategy])), { live: 'release-layout', tools: 'multi-app', sites: 'static-build', games: 'pnpm-build', openre: 'release-layout', network: 'git-checkout' });
+ assert.deepStrictEqual(Object.fromEntries(['live', 'tools', 'games', 'openre', 'network'].map((id) => [id, inv.services[id].strategy])), { live: 'release-layout', tools: 'multi-app', games: 'pnpm-build', openre: 'release-layout', network: 'git-checkout' });
// Live's and OpenRestream's release layouts are root-owned clones (git as root); their services and drills run as ubuntu.
// Live's and OpenRestream's release layouts are root-owned clones (git as root); their services and drills run as ubuntu.
for (const s of Object.values(inv.services)) assert.strictEqual(s.owner, ['live', 'openre'].includes(s.id) ? 'root' : 'ubuntu', `${s.id} checkout owner`);
for (const s of Object.values(inv.services)) assert.strictEqual(s.owner, ['live', 'openre'].includes(s.id) ? 'root' : 'ubuntu', `${s.id} checkout owner`);
assert.strictEqual(inv.services.live.runAs, 'ubuntu');
assert.strictEqual(inv.services.live.runAs, 'ubuntu');
@@ -34,7 +37,6 @@ runTests([
@@ -34,7 +37,6 @@ runTests([
assert.strictEqual(inv.services.live.codeDir, '/opt/openvibe.live/current');
assert.strictEqual(inv.services.live.codeDir, '/opt/openvibe.live/current');
assert.strictEqual(inv.services.live.release.git, '/opt/openvibe.live/repo');
assert.strictEqual(inv.services.live.release.git, '/opt/openvibe.live/repo');
assert.strictEqual(inv.services.openre.release.git, '/opt/openre.stream/repo');
assert.strictEqual(inv.services.openre.release.git, '/opt/openre.stream/repo');
- assert.deepStrictEqual(inv.services.sites.nginx.skipVhosts, ['openvibe.bot.conf']);
assert.deepStrictEqual([inv.services.bot.nginx.vhost, inv.services.bot.nginx.repoVhost, inv.services.bot.nginx.installOnDeploy], ['openvibe.bot.conf', 'deploy/nginx/openvibe.bot.conf', true]);
assert.deepStrictEqual([inv.services.bot.nginx.vhost, inv.services.bot.nginx.repoVhost, inv.services.bot.nginx.installOnDeploy], ['openvibe.bot.conf', 'deploy/nginx/openvibe.bot.conf', true]);
assert.strictEqual(inv.services.bot.installUnits, true);
assert.strictEqual(inv.services.bot.installUnits, true);
assert.strictEqual(inv.services.bot.enableUnits, true);
assert.strictEqual(inv.services.bot.enableUnits, true);