openvibers/openvibe.host Commits

Add the bot service to host.example.json for OpenVibe.Bot

OP OpenVibers committed 2 Oct 2026 · 9a62097289 · 4 files, +62 −4

Asked: Add a "bot" service to host.example.json so OpenVibe.Bot (openvibe.bot) can be provisioned. Copy the "codes" entry (around line 1955) exactly in shape and put it right after "ai"/before the next entry in the same services object, with: _note "Not deployed yet; unit, env file, port and database from…
Harness job host-add-a-bot-service-to-082924 (plan T15).

Changes

ddiff --git a/README.md b/README.md
iindex 5fb48c1..4c10278 100644
---- a/README.md
++++ b/README.md
@@@ -189,7 +189,7 @@ Each inventory entry's `drill` block declares:
- `countsTolerance` (default `0`): how many rows a busy table may differ by, because production keeps writing between the backup read and the drill;
- `supported: false` with a `reason` when a second instance cannot run without side effects.
--In `host.example.json`, 24 services have drill blocks, and 23 can be drilled. ai is marked unsupported, and the reason is in the file. live and media rely on their own drill switches (`LIVE_DRILL`, `MEDIA_DRILL`), which `requires` checks for in the deployed checkout. For a service with several units, `unit` (as tools does: only the docs app runs) or `command` (an absolute argv, as openre does: only `openre-api` runs) says which one process the drill starts. `productionPort` says which production port to compare against, `databases` entries can be data directories (`{ "env": "DATA_DIR", "dir": true }`) or a PostgreSQL database's two URL env vars, `bind` redirects paths a service opens relative to its checkout into the drill directory (`BindPaths=`, in the drill unit's own mount namespace), and `requires` refuses the drill unless the deployed checkout has each switch the overrides rely on. A PostgreSQL drill always overrides `DATABASE_URL`, `DATABASE_DIRECT_URL` and `VALKEY_URL` and drops its scratch database and role in a `finally` ([docs/restore-drills.md](docs/restore-drills.md#a-postgresql-drill)). The Tools and Games drills rely only on switches those repositories already have. [docs/restore-drills.md](docs/restore-drills.md) lists what each repository could add to make its drill stricter.
++In `host.example.json`, 25 services have drill blocks, and 24 can be drilled. ai is marked unsupported, and the reason is in the file. live and media rely on their own drill switches (`LIVE_DRILL`, `MEDIA_DRILL`), which `requires` checks for in the deployed checkout. For a service with several units, `unit` (as tools does: only the docs app runs) or `command` (an absolute argv, as openre does: only `openre-api` runs) says which one process the drill starts. `productionPort` says which production port to compare against, `databases` entries can be data directories (`{ "env": "DATA_DIR", "dir": true }`) or a PostgreSQL database's two URL env vars, `bind` redirects paths a service opens relative to its checkout into the drill directory (`BindPaths=`, in the drill unit's own mount namespace), and `requires` refuses the drill unless the deployed checkout has each switch the overrides rely on. A PostgreSQL drill always overrides `DATABASE_URL`, `DATABASE_DIRECT_URL` and `VALKEY_URL` and drops its scratch database and role in a `finally` ([docs/restore-drills.md](docs/restore-drills.md#a-postgresql-drill)). The Tools and Games drills rely only on switches those repositories already have. [docs/restore-drills.md](docs/restore-drills.md) lists what each repository could add to make its drill stricter.
The sandbox blocks writes outside the drill directory and addresses beyond loopback. Loopback stays open, so the overrides are what keep a drill away from production services. Common overrides are `OV_OAUTH_CLIENT_SECRET=` (no service tokens) and `http://127.0.0.1:9` for URLs whose empty value would fall back to a production service.
ddiff --git a/docs/db-inventory.md b/docs/db-inventory.md
iindex 5ee1797..c8cff77 100644
---- a/docs/db-inventory.md
++++ b/docs/db-inventory.md
@@@ -22,7 +22,7 @@ logical `.dump` feeds the drills ([backups.md](backups.md#postgresql-services)).
| Engine | Services |
|---|---|
--| PostgreSQL | tips, deals, coupons, reviews, trade, news, wiki, blog, search, sources, codes, vip, ai, host, events, community, billing, media, openre (19) |
++| PostgreSQL | tips, deals, coupons, reviews, trade, news, wiki, blog, search, sources, codes, vip, ai, bot, host, events, community, billing, media, openre (20) |
| SQLite | network, chat, tools, games, live (5) |
**The two engines appear in four more places in the inventory:**
ddiff --git a/host.example.json b/host.example.json
iindex a82b5b5..4421e4f 100644
---- a/host.example.json
++++ b/host.example.json
@@@ -1,6 +1,6 @@
{
"_about": "Example ovhost inventory for the single OpenVibe host (openvibe-oregon). Copy to /etc/openvibe/host.json (root:root, 0640) and correct anything that differs on the machine; the real file is never committed. Paths, units, ports and env file locations come from OpenVibe.Network docs/roadmap-baseline/data/services.json and prod-snapshot.json (22 Sep 2026) and each repository's deploy/ directory. env.required lists NAMES only.",
-- "_databases": "Databases (lib/inventory.js): each services.<id>.databases[] item is one of two engines. SQLite (the default) is a file the service user's worker copies: { \"name\": \"live\", \"path\": \"/opt/openvibe.live/data/live.db\" }. PostgreSQL is a database in the cluster: { \"name\": \"trade\", \"engine\": \"postgresql\", \"database\": \"ov_trade\" } \u2014 the name must match ov_[a-z0-9_]+, and there is no path (ovhost reaches it as the postgres OS user over peer auth, and pgBackRest, not ovhost, copies it). tips, deals, coupons, reviews, trade, news, wiki, blog, search, sources, codes, vip, ai, host, events, community, billing, media, openre and games are PostgreSQL; network, chat, tools and live are still SQLite. A drill block follows the engine: a PostgreSQL database maps to { \"url\": \"DATABASE_URL\", \"directUrl\": \"DATABASE_DIRECT_URL\" } and the drill creates a scratch database and role to restore into.",
++ "_databases": "Databases (lib/inventory.js): each services.<id>.databases[] item is one of two engines. SQLite (the default) is a file the service user's worker copies: { \"name\": \"live\", \"path\": \"/opt/openvibe.live/data/live.db\" }. PostgreSQL is a database in the cluster: { \"name\": \"trade\", \"engine\": \"postgresql\", \"database\": \"ov_trade\" } \u2014 the name must match ov_[a-z0-9_]+, and there is no path (ovhost reaches it as the postgres OS user over peer auth, and pgBackRest, not ovhost, copies it). tips, deals, coupons, reviews, trade, news, wiki, blog, search, sources, codes, vip, ai, bot, host, events, community, billing, media, openre and games are PostgreSQL; network, chat, tools and live are still SQLite. A drill block follows the engine: a PostgreSQL database maps to { \"url\": \"DATABASE_URL\", \"directUrl\": \"DATABASE_DIRECT_URL\" } and the drill creates a scratch database and role to restore into.",
"host": "openvibe-oregon",
"stateDir": "/var/lib/openvibe-host",
"backupDir": "/var/backups/openvibe",
@@@ -2109,6 +2109,64 @@
]
}
},
++ "bot": {
++ "_note": "Not deployed yet; unit, env file, port and database from OpenVibe.Bot's deploy/ directory.",
++ "repo": "/opt/openvibe.bot",
++ "units": [
++ "openvibe-bot.service"
++ ],
++ "unitSources": {
++ "openvibe-bot.service": "deploy/systemd/openvibe-bot.service"
++ },
++ "envFile": "/etc/openvibe/bot.env",
++ "env": {
++ "required": "from-example"
++ },
++ "port": 4630,
++ "ready": {
++ "url": "http://127.0.0.1:4630/api/ready",
++ "timeoutSeconds": 30
++ },
++ "noRestartPaths": [
++ "docs/",
++ "test/",
++ "**/*.md"
++ ],
++ "databases": [
++ {
++ "name": "bot",
++ "engine": "postgresql",
++ "database": "ov_bot"
++ }
++ ],
++ "drill": {
++ "port": 14630,
++ "databases": {
++ "bot": {
++ "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"
++ },
++ "compare": [],
++ "counts": [
++ {
++ "db": "bot",
++ "table": "robots"
++ },
++ {
++ "db": "bot",
++ "table": "devices"
++ }
++ ]
++ }
++ },
"openre": {
"_note": "Release layout: /opt/openre.stream/repo is the git clone, releases/<sha12> the releases (npm ci into each, chowned to ubuntu), `current` the link the API and the coordinator run from (deploy.sh release + api as ovhost deploy openre; ovhost rollback openre). units are the API and the session coordinator. workerUnits are the transport workers: one instance per release (openre-rtmp-ingest@<release>), started by deploy.sh workers (still the repository's script), drained by the coordinator when a newer generation is ready, and exiting on their own. ovhost lists them in status/validate, never starts, stops or restarts them (the inventory refuses a worker unit in units), and never prunes a release a worker instance runs from.",
"repo": "/opt/openre.stream",
ddiff --git a/test/cli-inventory.test.js b/test/cli-inventory.test.js
iindex 73c58d5..478e87e 100644
---- a/test/cli-inventory.test.js
++++ b/test/cli-inventory.test.js
@@@ -17,7 +17,7 @@ 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', 'openre']);
++ 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']);
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');