openvibers/openvibe.host Commits

List the OpenRe WHIP worker and correct the stale Bot note (#18)

OP OpenVibers committed 4 Oct 2026 · 91fac68488 · 2 files, +16 −8

checks run by GitHub CI (test, test-pg) · CI ✓ 3/3 · review SHIP by DeepSeek harness

https://github.com/OpenVibers/OpenVibe.Host/pull/18

Changes

ddiff --git a/host.example.json b/host.example.json
iindex 9725ee8..388e141 100644
---- a/host.example.json
++++ b/host.example.json
@@@ -2113,7 +2113,7 @@
}
},
"bot": {
-- "_note": "Not deployed yet. Bot owns openvibe.bot.conf: deploy bot installs its proxy vhost after Sites skips that filename. Before the first deploy, provision bot.env, the database and the existing TLS certificate; use ovhost deploy bot --restart if the checkout is already current so it installs and enables the service unit. Keep Sites on its ovhost deploy path (its legacy fallback still installs every vhost).",
++ "_note": "Deployed; migrations 0001 and 0002 applied. Bot owns openvibe.bot.conf: deploy bot installs its proxy vhost after Sites skips that filename. BOT_WHIP_BASE (in /etc/openvibe/bot.env) is the switch that points Bot's video at https://ingest.openre.stream/whip. Use ovhost deploy bot --restart if the checkout is already current so it installs and enables the service unit. Keep Sites on its ovhost deploy path (its legacy fallback still installs every vhost).",
"repo": "/opt/openvibe.bot",
"installUnits": true,
"enableUnits": true,
@@@ -2178,7 +2178,7 @@
}
},
"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>, openre-jsmpeg@<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.",
++ "_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>, openre-jsmpeg@<release>, openre-webrtc@<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. deploy.sh workers starts openre-webrtc@<release> once the WHIP vhost (ingest.openre.stream) and MEDIASOUP_ANNOUNCED_IP are set.",
"repo": "/opt/openre.stream",
"strategy": "release-layout",
"owner": "root",
@@@ -2197,7 +2197,8 @@
"workerUnits": [
],
"envFile": "/etc/openvibe/openre.env",
"env": {
ddiff --git a/test/openre-workers.test.js b/test/openre-workers.test.js
iindex 6e5e834..249bf3f 100644
---- a/test/openre-workers.test.js
++++ b/test/openre-workers.test.js
@@@ -15,7 +15,12 @@ const { normalise } = require('../lib/inventory');
const EXAMPLE = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'host.example.json'), 'utf8'));
const RELEASE = '655b98a10aaa';
const OLD = '0123456789ab';
--const WORKERS = [`openre-rtmp-ingest@${RELEASE}.service`, `openre-restream-worker@${RELEASE}.service`, `openre-jsmpeg@${RELEASE}.service`];
++const WORKERS = [
++ `openre-rtmp-ingest@${RELEASE}.service`,
++ `openre-restream-worker@${RELEASE}.service`,
++ `openre-jsmpeg@${RELEASE}.service`,
++ `openre-webrtc@${RELEASE}.service`,
++];
/** The standard fake host plus OpenRe as on openvibe-ovh: release layout, two units, worker instances. */
async function openreHost({ workersRunning = true } = {}) {
@@@ -49,6 +54,7 @@ async function openreHost({ workersRunning = true } = {}) {
host.addUnit(`openre-rtmp-ingest@${RELEASE}.service`, { mainPid: 3102, ...(workersRunning ? {} : { active: 'inactive', sub: 'dead' }) });
host.addUnit(`openre-restream-worker@${RELEASE}.service`, { mainPid: 3103, ...(workersRunning ? {} : { active: 'inactive', sub: 'dead' }) });
host.addUnit(`openre-jsmpeg@${RELEASE}.service`, { mainPid: 3104, ...(workersRunning ? {} : { active: 'inactive', sub: 'dead' }) });
++ host.addUnit(`openre-webrtc@${RELEASE}.service`, { mainPid: 3105, ...(workersRunning ? {} : { active: 'inactive', sub: 'dead' }) });
host.addUnit(`openre-rtmp-ingest@${OLD}.service`, { active: 'inactive', sub: 'dead', mainPid: 0 });
host.addUnit(`openre-jsmpeg@${OLD}.service`, { active: 'inactive', sub: 'dead', mainPid: 0 });
// openre is PostgreSQL: the production database and a healthy pgBackRest stanza so that
@@@ -89,7 +95,7 @@ runTests([
const inv = normalise(EXAMPLE);
const o = inv.services.openre;
assert.deepStrictEqual(o.units, ['openre-api.service', 'openre-session-coordinator.service']);
-- assert.deepStrictEqual(o.workerUnits, ['[email protected]', '[email protected]', '[email protected]']);
++ assert.deepStrictEqual(o.workerUnits, ['[email protected]', '[email protected]', '[email protected]', '[email protected]']);
assert.strictEqual(o.layout, 'release');
assert.strictEqual(o.strategy, 'release-layout');
assert.strictEqual(o.managed, true, 'ovhost deploy openre: release + api (WS-N task 11)');
@@@ -119,9 +125,9 @@ runTests([
const host = await openreHost();
const r = await host.cli('status', 'openre');
assert.strictEqual(r.code, 0, r.out);
-- assert.match(r.out, new RegExp(`openre\\s+${RELEASE}\\s+ready\\s+openre-api=active openre-session-coordinator=active workers\\[openre-rtmp-ingest@${RELEASE}=active openre-rtmp-ingest@${OLD}=inactive openre-restream-worker@${RELEASE}=active openre-jsmpeg@${RELEASE}=active openre-jsmpeg@${OLD}=inactive\\] ingest sessions=0$`, 'm'));
++ assert.match(r.out, new RegExp(`openre\\s+${RELEASE}\\s+ready\\s+openre-api=active openre-session-coordinator=active workers\\[openre-rtmp-ingest@${RELEASE}=active openre-rtmp-ingest@${OLD}=inactive openre-restream-worker@${RELEASE}=active openre-jsmpeg@${RELEASE}=active openre-jsmpeg@${OLD}=inactive openre-webrtc@${RELEASE}=active\\] ingest sessions=0$`, 'm'));
const json = JSON.parse((await host.cli('status', 'openre', '--json')).out);
-- assert.deepStrictEqual(json[0].workers.map((w) => [w.unit, w.active]), [[`openre-rtmp-ingest@${RELEASE}.service`, 'active'], [`openre-rtmp-ingest@${OLD}.service`, 'inactive'], [`openre-restream-worker@${RELEASE}.service`, 'active'], [`openre-jsmpeg@${RELEASE}.service`, 'active'], [`openre-jsmpeg@${OLD}.service`, 'inactive']]);
++ assert.deepStrictEqual(json[0].workers.map((w) => [w.unit, w.active]), [[`openre-rtmp-ingest@${RELEASE}.service`, 'active'], [`openre-rtmp-ingest@${OLD}.service`, 'inactive'], [`openre-restream-worker@${RELEASE}.service`, 'active'], [`openre-jsmpeg@${RELEASE}.service`, 'active'], [`openre-jsmpeg@${OLD}.service`, 'inactive'], [`openre-webrtc@${RELEASE}.service`, 'active']]);
const lists = host.calls.filter((c) => c.cmd === 'systemctl' && c.args[0] === 'list-units');
assert.deepStrictEqual(lists[0].args, ['list-units', '--all', '--plain', '--no-legend', '--no-pager', 'openre-rtmp-ingest@*.service']);
assert.ok(lists.every((c) => !c.privileged), 'listing needs no privileges');
@@@ -132,7 +138,7 @@ runTests([
let host = await openreHost();
let res = JSON.parse((await host.cli('validate', 'openre', '--json')).out);
const workers = res.findings.filter((f) => f.area === 'units' && /^worker /.test(f.message));
-- assert.deepStrictEqual(workers.map((f) => f.level), ['info', 'info', 'info', 'info', 'info']);
++ assert.deepStrictEqual(workers.map((f) => f.level), ['info', 'info', 'info', 'info', 'info', 'info']);
assert.match(workers[0].message, /never starts, stops or restarts it/);
assert.ok(!res.findings.some((f) => /no running instance/.test(f.message)));
assert.deepStrictEqual(stateChanges(host), []);
@@@ -142,6 +148,7 @@ runTests([
['warn', 'no running instance of worker unit [email protected]'],
['warn', 'no running instance of worker unit [email protected]'],
['warn', 'no running instance of worker unit [email protected]'],
++ ['warn', 'no running instance of worker unit [email protected]'],
]);
assert.deepStrictEqual(stateChanges(host), []);
assert.ok(!JSON.stringify(res).includes(SECRET));