openvibers/openrestream Commits
README and STATUS.json are current (roadmap WS-U task 3): a Capabilities section lists the seven openre.* capabilities and what OpenRe calls at Events, Live (live.lineage.resolve, also added to the grants list) and Media; Security notes point to SECURITY.md; Deploying says how an API and a worker deploy roll back. Stale facts corrected: the status line and launch rule say the RTMP ingest is public on 1936 and Live is wired (phase A of docs/cutover.md, 2026-09-23), not loopback-only and inert; Depends on names the current pins (contracts v0.49.0, sdk v0.12.0, shared v1.22.0). STATUS.json names openvibe-contracts v0.49.0, fixes the livePatch note, gains features and is updated 2026-09-28.
Changes
@@@ -2,7 +2,7 @@> Ingest and restream: stream definitions, keys, sessions, transport workers, outputs and output health.--**Status:** alpha (roadmap Wave 7). Tested end to end with real RTMP. Deployed internally on `openvibe-ovh` since 2026-09-23 (API on 127.0.0.1:4500, RTMP ingest bound to 127.0.0.1:1936, one rehearsal broadcast passed), but not launched: no broadcaster uses it (all 102 Live slots are still `ingest_authority=live`), and Live's side of the integration is deployed but inert until `OPENRE_*` is set in Live. The domain keeps its placeholder page on OpenVibe.Sites until the launch rule below is met.++**Status:** alpha (roadmap Wave 7). Tested end to end with real RTMP. Deployed internally on `openvibe-ovh` since 2026-09-23 (API and coordinator on 127.0.0.1:4500; RTMP ingest public on port 1936 at `ingest.openre.stream` since the cutover runbook's phase A on 2026-09-23; Live wired with `OPENRE_URL` and an `openre.session.*` subscription; one rehearsal broadcast passed), but not launched: no broadcaster uses it, since every Live slot is still ingested by Live until its per-slot cutover (`docs/cutover.md` phase B). The domain keeps its placeholder page on OpenVibe.Sites until the launch rule below is met.**Domain:** `openre.stream` (UI + API), `ingest.openre.stream` (RTMP, DNS only)**Plan:** OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §10, §10.5, §15.10; ADR-009 (binding), ADR-004, ADR-006, ADR-007.**License:** AGPL-3.0 (same as every OpenVibe service).@@@ -32,7 +32,8 @@ The ingest/restream control plane and runtime extracted from OpenVibe.Live. Live- OpenVibe.Network (JWKS for service tokens and user JWTs; OAuth client `openre`; client-credentials tokens for Events/Media)- OpenVibe.Events (event relay; optional: rows wait in the outbox)- OpenVibe.Media (recording requests; optional: sessions work without it)--- OpenVibe.Contracts (ids, problem+json, capability checks), OpenVibe.SDK v0.2.2 (outbox, token client), OpenVibe.Shared (chrome)++- OpenVibe.Contracts v0.49.0 (ids, problem+json, capability checks), OpenVibe.SDK v0.12.0 (outbox, token client, per-actor limits), OpenVibe.Shared v1.22.0 (chrome), pinned by release tarball++- OpenVibe.Live (`live.lineage.resolve`: which channel a Live-linked stream belongs to)- node-media-server 2.7.4 (the RTMP session code Live runs) and the system ffmpeg## What is ported, per protocol@@@ -79,6 +80,23 @@ npm test # every test/*.test.js; the RTMP e2e test needNode 22 (≥ 22.12 for `reusePort`): `fnm exec --using=22.22.1 npm test`.++## Capabilities++++Implemented here (the service manifest's `capabilities`, audience `openvibe.openre`; routes under++[Auth](#auth)): `openre.stream.read`, `openre.stream.write`, `openre.key.rotate`,++`openre.session.read`, `openre.session.end`, `openre.output.read` and `openre.output.write`.++++Called elsewhere, as the service principal `openre` (client credentials from Network):++++| Service | Grant | Why |++|---|---|---|++| OpenVibe.Events | `events.event.publish` | the coordinator's outbox relay (`openre.*` events) |++| OpenVibe.Live | `live.lineage.resolve` | the channel a Live-linked stream definition belongs to |++| OpenVibe.Media | the `live` tenant key today (`MEDIA_API_KEY`); `media.object.upload` once `OPENRE_MEDIA_AUTH=service` | recording requests |++++The full grant list, including what Live needs to call OpenRe, is under "Grants the lead adds in++Network" below.++## AuthServices call with an OpenVibe.Network client-credentials token (audience `openvibe.openre`), verified offline against the Network JWKS; each route checks one capability. A service may send `X-OV-Subject: usr_…` to act for one owner (it is then limited to that owner's streams). Browsers/owners use the Network user JWT (`ov_token` cookie from `/auth/login`, or Bearer) and act on their own streams; Network role `admin` is staff (read all, end sessions).@@@ -93,12 +111,13 @@ Services call with an OpenVibe.Network client-credentials token (audience `openv| `openre.output.read` | `GET /api/v1/streams/:id/destinations`, `GET /api/v1/sessions/:id/outputs`, `GET /api/v1/destinations/:id/logs`, `GET /api/v1/outputs/:id/logs` || `openre.output.write` | `POST /api/v1/streams/:id/destinations`, `PATCH`/`DELETE /api/v1/destinations/:id`, `POST /api/v1/destinations/:id/test|start|stop` |--The ids were proposed in [docs/capabilities-proposal/](docs/capabilities-proposal/) with the service manifest ([docs/service-manifest-proposal.json](docs/service-manifest-proposal.json)) and are registered in `openvibe-contracts` since v0.16.0 (this repository pins v0.33.0). `server/auth/index.js` grants them with the contracts rule (exact id or `family.*`) and defers to `capabilities.check()` for every id the installed contracts know. Errors are RFC 9457 problem+json with a stable `code`.++The ids were proposed in [docs/capabilities-proposal/](docs/capabilities-proposal/) with the service manifest ([docs/service-manifest-proposal.json](docs/service-manifest-proposal.json)) and are registered in `openvibe-contracts` since v0.16.0 (this repository pins v0.49.0). `server/auth/index.js` grants them with the contracts rule (exact id or `family.*`) and defers to `capabilities.check()` for every id the installed contracts know. Errors are RFC 9457 problem+json with a stable `code`.**Grants the lead adds in Network** (`[client, capability, audience]`):- `[live, openre.stream.read, openvibe.openre]`, `[live, openre.stream.write, openvibe.openre]`, `[live, openre.key.rotate, openvibe.openre]`, `[live, openre.session.read, openvibe.openre]`- `[openre, events.event.publish, openvibe.events]`++- `[openre, live.lineage.resolve, openvibe.live]` (the channel lineage of Live-linked streams)- `[live, events.subscription.manage, openvibe.events]` if Live creates its own `openre.session.*` subscription (or an operator creates it)- Media, only when `OPENRE_MEDIA_AUTH=service`: `[openre, media.object.upload, openvibe.media]` with namespace `live` — and Media must first name that capability on its VOD create/ingest/finalize/delete routes (today they accept only the tenant app key; see "Recording").- OAuth client `openre` (authorization code + client credentials) with redirect `https://openre.stream/auth/callback`.@@@ -126,6 +145,8 @@ When a session is live and its definition says `recording_mode` `vod` or `clips`## Security notes++Reporting a vulnerability: [SECURITY.md](SECURITY.md).++- Ingest keys: 256-bit random, `ork_` + 43 base64url characters, SHA-256 at rest, compared by indexed lookup inside the RTMP handshake. Never logged (only the last four characters), never in an event, never in a URL after the handshake (the publish is renamed to the session id).- Destination keys and SRT passphrases: AES-256-GCM (`OPENRE_SECRETS_KEY`, rotation via `OPENRE_SECRETS_KEY_PREVIOUS`); APIs return `****` + last four only; ffmpeg command lines are logged redacted.- SSRF (plan anti-goal 13): destination URLs must be `rtmp://`, `rtmps://` or `srt://` with a hostname, no credentials, and no loopback/private/link-local/CGNAT/multicast host — checked on write and again, with DNS resolution, right before ffmpeg starts (`OPENRE_DEST_ALLOW_PRIVATE` opts out, e.g. a LAN box). Live allowed private hosts; the migration holds such destinations with the reason recorded.@@@ -164,6 +185,10 @@ sudo deploy/scripts/deploy.sh status # deploy-legacy.sh- A **worker deploy** starts `openre-rtmp-ingest@<sha>` and `openre-restream-worker@<sha>`; older instances are disabled (not stopped) and exit on their own when drained. Never `systemctl restart` a worker instance during a broadcast; `systemctl stop` starts a drain and waits up to 30 min (`TimeoutStopSec`), then kills.- Worker instances are named after the release. `deploy.sh workers <sha>` for a release that already runs a generation does nothing. An env change that workers read, such as `OPENRE_RTMP_BIND`, takes effect with the next release's generation (docs/cutover.md A3).- **Restore drills** (`ovhost drill openre`, OpenVibe.Host) start only `openre-api`, with `OPENRE_DRILL=1`, on a restored copy of the database. In drill mode the API serves reads and refuses writes, `/play/` and sign-in with 503 `openre.drill_read_only`. The coordinator and every worker exit before they open the database. The event relay and Media calls are off (`test/drill-mode.test.js`). Never set `OPENRE_DRILL` in `/etc/openvibe/openre.env`; the preflight's `env` check fails if it is set.++- Rollback: when `openre-api` is not ready within 60 s ovhost switches `current` back to the previous++ release by itself; afterwards `sudo deploy/scripts/deploy.sh rollback [<sha>]` (`ovhost rollback++ openre`). Workers roll back by starting the previous generation (`deploy.sh workers <sha>`); the++ coordinator drains the newer one. The schema code only adds, so an older release reads a newer database.- First install: create the `openre` OAuth client and grants in Network, `/etc/openvibe/openre.env` (0600), the certificate for `openre.stream`, DNS for `openre.stream` (proxied) and `ingest.openre.stream` (DNS only), firewall 1936/tcp, then `release`, `api`, `workers`, and `systemctl enable --now openre-api openre-session-coordinator`.## Live integration (patch)@@@ -239,8 +264,8 @@ JavaScript; real persistence and end-to-end workflows; capability and event regisitemap/robots/feed behaviour; acceptance tests proving the advertised functionality. Today theruntime, identity integration, server-rendered routes, persistence, capability registration (contracts v0.16.0),the migration strategy and the acceptance tests are in place, and the service is deployed--internally (loopback only). An independent security review, a public ingest port and the first--slot cutover are not.++internally with its public ingest port open. An independent security review and the first slot++cutover are not.---@@@ -8,7 +8,7 @@"port": 4500,"rtmpPort": 1936,"domain": "openre.stream",-- "contracts": "openvibe-contracts v0.33.0 (openvibe-sdk v0.5.0, openvibe-shared v1.5.1)",++ "contracts": "openvibe-contracts v0.49.0 (openvibe-sdk v0.12.0, openvibe-shared v1.22.0)","protocols": {"rtmp": "ingest ported (openre-rtmp-ingest)","restream": "RTMP source to RTMP/RTMPS/SRT destinations ported (openre-restream-worker)",@@@ -16,18 +16,23 @@"webrtc-sfu": "not ported (worker interface only)","jsmpeg": "not ported (worker interface only)"},-- "capabilities": [-- "openre.stream.read",-- "openre.stream.write",-- "openre.key.rotate",-- "openre.session.read",-- "openre.session.end",-- "openre.output.read",-- "openre.output.write"-- ],++ "capabilities": ["openre.stream.read", "openre.stream.write", "openre.key.rotate", "openre.session.read", "openre.session.end", "openre.output.read", "openre.output.write"],"capabilitiesRegistered": true,-- "livePatch": "applied in Live f0ca18b and deployed, but inert: live.env has no OPENRE_URL/OPENRE_PUBLIC_URL/OPENRE_EVENTS_SECRET and there is no Events subscription openre.session.* -> Live; 102 of 102 Live slots have ingest_authority=live, so no broadcaster uses OpenRe",++ "livePatch": "applied in Live f0ca18b and deployed; Live wired on 2026-09-23 (OPENRE_URL and the openre.session.* subscription, docs/cutover.md phase A); every Live slot keeps ingest_authority=live until its per-slot cutover (phase B), so no broadcaster uses OpenRe yet","deployed": true,-- "deployment": "Production release e0dc1b46fd99 (2026-09-23 19:23 UTC): API + coordinator on 127.0.0.1:4500, RTMP ingest public on 0.0.0.0:1936 (ingest.openre.stream, DNS-only; reachable from outside), Live wired (OPENRE_URL, openre.session.* subscription), preflight all PASS, restore drill passed. No slot switched yet: every slot is still ingested by Live until the per-slot cutover (docs/cutover.md phase B).",-- "updated": "2026-09-23"++ "deployment": "Deployed with `sudo ovhost deploy openre` (OpenVibe.Host strategy release-layout since the 2026-09-27 cutover; deploy/scripts/deploy.sh is the wrapper and keeps workers/status/prune): API + coordinator on 127.0.0.1:4500, RTMP ingest public on 0.0.0.0:1936 (ingest.openre.stream, DNS-only; reachable from outside since 2026-09-23), Live wired (OPENRE_URL, openre.session.* subscription), preflight all PASS, restore drill passed. No slot switched yet: every slot is still ingested by Live until the per-slot cutover (docs/cutover.md phase B).",++ "updated": "2026-09-28",++ "features": [++ "stream definitions owned by a Network subject, with typed references",++ "ingest keys hashed at rest, shown once, rotation with grace",++ "RTMP ingest workers with generations, leases and drains that survive API deploys",++ "RTMP/RTMPS/SRT restream with output health and a circuit breaker",++ "SSRF-checked destinations with sealed keys",++ "recording requests to OpenVibe.Media",++ "HTTP-FLV playback descriptors",++ "outbox events openre.session.*, openre.output.*, openre.recording.requested, openre.key.rotated",++ "migration from Live without importing old keys",++ "restore-drill read-only mode",++ "per-actor limits (openvibe-sdk/limits)"++ ]}
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
> Ingest and restream: stream definitions, keys, sessions, transport workers, outputs and output health.
> Ingest and restream: stream definitions, keys, sessions, transport workers, outputs and output health.
-**Status:** alpha (roadmap Wave 7). Tested end to end with real RTMP. Deployed internally on `openvibe-ovh` since 2026-09-23 (API on 127.0.0.1:4500, RTMP ingest bound to 127.0.0.1:1936, one rehearsal broadcast passed), but not launched: no broadcaster uses it (all 102 Live slots are still `ingest_authority=live`), and Live's side of the integration is deployed but inert until `OPENRE_*` is set in Live. The domain keeps its placeholder page on OpenVibe.Sites until the launch rule below is met.
+**Status:** alpha (roadmap Wave 7). Tested end to end with real RTMP. Deployed internally on `openvibe-ovh` since 2026-09-23 (API and coordinator on 127.0.0.1:4500; RTMP ingest public on port 1936 at `ingest.openre.stream` since the cutover runbook's phase A on 2026-09-23; Live wired with `OPENRE_URL` and an `openre.session.*` subscription; one rehearsal broadcast passed), but not launched: no broadcaster uses it, since every Live slot is still ingested by Live until its per-slot cutover (`docs/cutover.md` phase B). The domain keeps its placeholder page on OpenVibe.Sites until the launch rule below is met.
**Domain:** `openre.stream` (UI + API), `ingest.openre.stream` (RTMP, DNS only)
**Domain:** `openre.stream` (UI + API), `ingest.openre.stream` (RTMP, DNS only)
**Plan:** OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §10, §10.5, §15.10; ADR-009 (binding), ADR-004, ADR-006, ADR-007.
**Plan:** OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §10, §10.5, §15.10; ADR-009 (binding), ADR-004, ADR-006, ADR-007.
**License:** AGPL-3.0 (same as every OpenVibe service).
**License:** AGPL-3.0 (same as every OpenVibe service).
@@ -32,7 +32,8 @@ The ingest/restream control plane and runtime extracted from OpenVibe.Live. Live
@@ -32,7 +32,8 @@ The ingest/restream control plane and runtime extracted from OpenVibe.Live. Live
- OpenVibe.Network (JWKS for service tokens and user JWTs; OAuth client `openre`; client-credentials tokens for Events/Media)
- OpenVibe.Network (JWKS for service tokens and user JWTs; OAuth client `openre`; client-credentials tokens for Events/Media)
- OpenVibe.Events (event relay; optional: rows wait in the outbox)
- OpenVibe.Events (event relay; optional: rows wait in the outbox)
- OpenVibe.Media (recording requests; optional: sessions work without it)
- OpenVibe.Media (recording requests; optional: sessions work without it)
-- OpenVibe.Contracts (ids, problem+json, capability checks), OpenVibe.SDK v0.2.2 (outbox, token client), OpenVibe.Shared (chrome)
+- OpenVibe.Contracts v0.49.0 (ids, problem+json, capability checks), OpenVibe.SDK v0.12.0 (outbox, token client, per-actor limits), OpenVibe.Shared v1.22.0 (chrome), pinned by release tarball
+- OpenVibe.Live (`live.lineage.resolve`: which channel a Live-linked stream belongs to)
- node-media-server 2.7.4 (the RTMP session code Live runs) and the system ffmpeg
- node-media-server 2.7.4 (the RTMP session code Live runs) and the system ffmpeg
## What is ported, per protocol
## What is ported, per protocol
@@ -79,6 +80,23 @@ npm test # every test/*.test.js; the RTMP e2e test need
@@ -79,6 +80,23 @@ npm test # every test/*.test.js; the RTMP e2e test need
Node 22 (≥ 22.12 for `reusePort`): `fnm exec --using=22.22.1 npm test`.
Node 22 (≥ 22.12 for `reusePort`): `fnm exec --using=22.22.1 npm test`.
+## Capabilities
+
+Implemented here (the service manifest's `capabilities`, audience `openvibe.openre`; routes under
+[Auth](#auth)): `openre.stream.read`, `openre.stream.write`, `openre.key.rotate`,
+`openre.session.read`, `openre.session.end`, `openre.output.read` and `openre.output.write`.
+
+Called elsewhere, as the service principal `openre` (client credentials from Network):
+
+| Service | Grant | Why |
+|---|---|---|
+| OpenVibe.Events | `events.event.publish` | the coordinator's outbox relay (`openre.*` events) |
+| OpenVibe.Live | `live.lineage.resolve` | the channel a Live-linked stream definition belongs to |
+| OpenVibe.Media | the `live` tenant key today (`MEDIA_API_KEY`); `media.object.upload` once `OPENRE_MEDIA_AUTH=service` | recording requests |
+
+The full grant list, including what Live needs to call OpenRe, is under "Grants the lead adds in
+Network" below.
+
## Auth
## Auth
Services call with an OpenVibe.Network client-credentials token (audience `openvibe.openre`), verified offline against the Network JWKS; each route checks one capability. A service may send `X-OV-Subject: usr_…` to act for one owner (it is then limited to that owner's streams). Browsers/owners use the Network user JWT (`ov_token` cookie from `/auth/login`, or Bearer) and act on their own streams; Network role `admin` is staff (read all, end sessions).
Services call with an OpenVibe.Network client-credentials token (audience `openvibe.openre`), verified offline against the Network JWKS; each route checks one capability. A service may send `X-OV-Subject: usr_…` to act for one owner (it is then limited to that owner's streams). Browsers/owners use the Network user JWT (`ov_token` cookie from `/auth/login`, or Bearer) and act on their own streams; Network role `admin` is staff (read all, end sessions).
@@ -93,12 +111,13 @@ Services call with an OpenVibe.Network client-credentials token (audience `openv
@@ -93,12 +111,13 @@ Services call with an OpenVibe.Network client-credentials token (audience `openv
| `openre.output.read` | `GET /api/v1/streams/:id/destinations`, `GET /api/v1/sessions/:id/outputs`, `GET /api/v1/destinations/:id/logs`, `GET /api/v1/outputs/:id/logs` |
| `openre.output.read` | `GET /api/v1/streams/:id/destinations`, `GET /api/v1/sessions/:id/outputs`, `GET /api/v1/destinations/:id/logs`, `GET /api/v1/outputs/:id/logs` |
| `openre.output.write` | `POST /api/v1/streams/:id/destinations`, `PATCH`/`DELETE /api/v1/destinations/:id`, `POST /api/v1/destinations/:id/test|start|stop` |
| `openre.output.write` | `POST /api/v1/streams/:id/destinations`, `PATCH`/`DELETE /api/v1/destinations/:id`, `POST /api/v1/destinations/:id/test|start|stop` |
-The ids were proposed in [docs/capabilities-proposal/](docs/capabilities-proposal/) with the service manifest ([docs/service-manifest-proposal.json](docs/service-manifest-proposal.json)) and are registered in `openvibe-contracts` since v0.16.0 (this repository pins v0.33.0). `server/auth/index.js` grants them with the contracts rule (exact id or `family.*`) and defers to `capabilities.check()` for every id the installed contracts know. Errors are RFC 9457 problem+json with a stable `code`.
+The ids were proposed in [docs/capabilities-proposal/](docs/capabilities-proposal/) with the service manifest ([docs/service-manifest-proposal.json](docs/service-manifest-proposal.json)) and are registered in `openvibe-contracts` since v0.16.0 (this repository pins v0.49.0). `server/auth/index.js` grants them with the contracts rule (exact id or `family.*`) and defers to `capabilities.check()` for every id the installed contracts know. Errors are RFC 9457 problem+json with a stable `code`.
**Grants the lead adds in Network** (`[client, capability, audience]`):
**Grants the lead adds in Network** (`[client, capability, audience]`):
- `[live, openre.stream.read, openvibe.openre]`, `[live, openre.stream.write, openvibe.openre]`, `[live, openre.key.rotate, openvibe.openre]`, `[live, openre.session.read, openvibe.openre]`
- `[live, openre.stream.read, openvibe.openre]`, `[live, openre.stream.write, openvibe.openre]`, `[live, openre.key.rotate, openvibe.openre]`, `[live, openre.session.read, openvibe.openre]`
- `[openre, events.event.publish, openvibe.events]`
- `[openre, events.event.publish, openvibe.events]`
+- `[openre, live.lineage.resolve, openvibe.live]` (the channel lineage of Live-linked streams)
- `[live, events.subscription.manage, openvibe.events]` if Live creates its own `openre.session.*` subscription (or an operator creates it)
- `[live, events.subscription.manage, openvibe.events]` if Live creates its own `openre.session.*` subscription (or an operator creates it)
- Media, only when `OPENRE_MEDIA_AUTH=service`: `[openre, media.object.upload, openvibe.media]` with namespace `live` — and Media must first name that capability on its VOD create/ingest/finalize/delete routes (today they accept only the tenant app key; see "Recording").
- Media, only when `OPENRE_MEDIA_AUTH=service`: `[openre, media.object.upload, openvibe.media]` with namespace `live` — and Media must first name that capability on its VOD create/ingest/finalize/delete routes (today they accept only the tenant app key; see "Recording").
- OAuth client `openre` (authorization code + client credentials) with redirect `https://openre.stream/auth/callback`.
- OAuth client `openre` (authorization code + client credentials) with redirect `https://openre.stream/auth/callback`.
@@ -126,6 +145,8 @@ When a session is live and its definition says `recording_mode` `vod` or `clips`
@@ -126,6 +145,8 @@ When a session is live and its definition says `recording_mode` `vod` or `clips`
## Security notes
## Security notes
+Reporting a vulnerability: [SECURITY.md](SECURITY.md).
+
- Ingest keys: 256-bit random, `ork_` + 43 base64url characters, SHA-256 at rest, compared by indexed lookup inside the RTMP handshake. Never logged (only the last four characters), never in an event, never in a URL after the handshake (the publish is renamed to the session id).
- Ingest keys: 256-bit random, `ork_` + 43 base64url characters, SHA-256 at rest, compared by indexed lookup inside the RTMP handshake. Never logged (only the last four characters), never in an event, never in a URL after the handshake (the publish is renamed to the session id).
- Destination keys and SRT passphrases: AES-256-GCM (`OPENRE_SECRETS_KEY`, rotation via `OPENRE_SECRETS_KEY_PREVIOUS`); APIs return `****` + last four only; ffmpeg command lines are logged redacted.
- Destination keys and SRT passphrases: AES-256-GCM (`OPENRE_SECRETS_KEY`, rotation via `OPENRE_SECRETS_KEY_PREVIOUS`); APIs return `****` + last four only; ffmpeg command lines are logged redacted.
- SSRF (plan anti-goal 13): destination URLs must be `rtmp://`, `rtmps://` or `srt://` with a hostname, no credentials, and no loopback/private/link-local/CGNAT/multicast host — checked on write and again, with DNS resolution, right before ffmpeg starts (`OPENRE_DEST_ALLOW_PRIVATE` opts out, e.g. a LAN box). Live allowed private hosts; the migration holds such destinations with the reason recorded.
- SSRF (plan anti-goal 13): destination URLs must be `rtmp://`, `rtmps://` or `srt://` with a hostname, no credentials, and no loopback/private/link-local/CGNAT/multicast host — checked on write and again, with DNS resolution, right before ffmpeg starts (`OPENRE_DEST_ALLOW_PRIVATE` opts out, e.g. a LAN box). Live allowed private hosts; the migration holds such destinations with the reason recorded.
@@ -164,6 +185,10 @@ sudo deploy/scripts/deploy.sh status # deploy-legacy.sh
@@ -164,6 +185,10 @@ sudo deploy/scripts/deploy.sh status # deploy-legacy.sh
- A **worker deploy** starts `openre-rtmp-ingest@<sha>` and `openre-restream-worker@<sha>`; older instances are disabled (not stopped) and exit on their own when drained. Never `systemctl restart` a worker instance during a broadcast; `systemctl stop` starts a drain and waits up to 30 min (`TimeoutStopSec`), then kills.
- A **worker deploy** starts `openre-rtmp-ingest@<sha>` and `openre-restream-worker@<sha>`; older instances are disabled (not stopped) and exit on their own when drained. Never `systemctl restart` a worker instance during a broadcast; `systemctl stop` starts a drain and waits up to 30 min (`TimeoutStopSec`), then kills.
- Worker instances are named after the release. `deploy.sh workers <sha>` for a release that already runs a generation does nothing. An env change that workers read, such as `OPENRE_RTMP_BIND`, takes effect with the next release's generation (docs/cutover.md A3).
- Worker instances are named after the release. `deploy.sh workers <sha>` for a release that already runs a generation does nothing. An env change that workers read, such as `OPENRE_RTMP_BIND`, takes effect with the next release's generation (docs/cutover.md A3).
- **Restore drills** (`ovhost drill openre`, OpenVibe.Host) start only `openre-api`, with `OPENRE_DRILL=1`, on a restored copy of the database. In drill mode the API serves reads and refuses writes, `/play/` and sign-in with 503 `openre.drill_read_only`. The coordinator and every worker exit before they open the database. The event relay and Media calls are off (`test/drill-mode.test.js`). Never set `OPENRE_DRILL` in `/etc/openvibe/openre.env`; the preflight's `env` check fails if it is set.
- **Restore drills** (`ovhost drill openre`, OpenVibe.Host) start only `openre-api`, with `OPENRE_DRILL=1`, on a restored copy of the database. In drill mode the API serves reads and refuses writes, `/play/` and sign-in with 503 `openre.drill_read_only`. The coordinator and every worker exit before they open the database. The event relay and Media calls are off (`test/drill-mode.test.js`). Never set `OPENRE_DRILL` in `/etc/openvibe/openre.env`; the preflight's `env` check fails if it is set.
+- Rollback: when `openre-api` is not ready within 60 s ovhost switches `current` back to the previous
+ release by itself; afterwards `sudo deploy/scripts/deploy.sh rollback [<sha>]` (`ovhost rollback
+ openre`). Workers roll back by starting the previous generation (`deploy.sh workers <sha>`); the
+ coordinator drains the newer one. The schema code only adds, so an older release reads a newer database.
- First install: create the `openre` OAuth client and grants in Network, `/etc/openvibe/openre.env` (0600), the certificate for `openre.stream`, DNS for `openre.stream` (proxied) and `ingest.openre.stream` (DNS only), firewall 1936/tcp, then `release`, `api`, `workers`, and `systemctl enable --now openre-api openre-session-coordinator`.
- First install: create the `openre` OAuth client and grants in Network, `/etc/openvibe/openre.env` (0600), the certificate for `openre.stream`, DNS for `openre.stream` (proxied) and `ingest.openre.stream` (DNS only), firewall 1936/tcp, then `release`, `api`, `workers`, and `systemctl enable --now openre-api openre-session-coordinator`.
## Live integration (patch)
## Live integration (patch)
@@ -239,8 +264,8 @@ JavaScript; real persistence and end-to-end workflows; capability and event regi
@@ -239,8 +264,8 @@ JavaScript; real persistence and end-to-end workflows; capability and event regi
sitemap/robots/feed behaviour; acceptance tests proving the advertised functionality. Today the
sitemap/robots/feed behaviour; acceptance tests proving the advertised functionality. Today the
runtime, identity integration, server-rendered routes, persistence, capability registration (contracts v0.16.0),
runtime, identity integration, server-rendered routes, persistence, capability registration (contracts v0.16.0),
the migration strategy and the acceptance tests are in place, and the service is deployed
the migration strategy and the acceptance tests are in place, and the service is deployed
-internally (loopback only). An independent security review, a public ingest port and the first
-slot cutover are not.
+internally with its public ingest port open. An independent security review and the first slot
+cutover are not.
---
---
@@ -8,7 +8,7 @@
@@ -8,7 +8,7 @@
"port": 4500,
"port": 4500,
"rtmpPort": 1936,
"rtmpPort": 1936,
"domain": "openre.stream",
"domain": "openre.stream",
- "contracts": "openvibe-contracts v0.33.0 (openvibe-sdk v0.5.0, openvibe-shared v1.5.1)",
+ "contracts": "openvibe-contracts v0.49.0 (openvibe-sdk v0.12.0, openvibe-shared v1.22.0)",
"protocols": {
"protocols": {
"rtmp": "ingest ported (openre-rtmp-ingest)",
"rtmp": "ingest ported (openre-rtmp-ingest)",
"restream": "RTMP source to RTMP/RTMPS/SRT destinations ported (openre-restream-worker)",
"restream": "RTMP source to RTMP/RTMPS/SRT destinations ported (openre-restream-worker)",
@@ -16,18 +16,23 @@
@@ -16,18 +16,23 @@
"webrtc-sfu": "not ported (worker interface only)",
"webrtc-sfu": "not ported (worker interface only)",
"jsmpeg": "not ported (worker interface only)"
"jsmpeg": "not ported (worker interface only)"
},
},
- "capabilities": [
- "openre.stream.read",
- "openre.stream.write",
- "openre.key.rotate",
- "openre.session.read",
- "openre.session.end",
- "openre.output.read",
- "openre.output.write"
- ],
+ "capabilities": ["openre.stream.read", "openre.stream.write", "openre.key.rotate", "openre.session.read", "openre.session.end", "openre.output.read", "openre.output.write"],
"capabilitiesRegistered": true,
"capabilitiesRegistered": true,
- "livePatch": "applied in Live f0ca18b and deployed, but inert: live.env has no OPENRE_URL/OPENRE_PUBLIC_URL/OPENRE_EVENTS_SECRET and there is no Events subscription openre.session.* -> Live; 102 of 102 Live slots have ingest_authority=live, so no broadcaster uses OpenRe",
+ "livePatch": "applied in Live f0ca18b and deployed; Live wired on 2026-09-23 (OPENRE_URL and the openre.session.* subscription, docs/cutover.md phase A); every Live slot keeps ingest_authority=live until its per-slot cutover (phase B), so no broadcaster uses OpenRe yet",
"deployed": true,
"deployed": true,
- "deployment": "Production release e0dc1b46fd99 (2026-09-23 19:23 UTC): API + coordinator on 127.0.0.1:4500, RTMP ingest public on 0.0.0.0:1936 (ingest.openre.stream, DNS-only; reachable from outside), Live wired (OPENRE_URL, openre.session.* subscription), preflight all PASS, restore drill passed. No slot switched yet: every slot is still ingested by Live until the per-slot cutover (docs/cutover.md phase B).",
- "updated": "2026-09-23"
+ "deployment": "Deployed with `sudo ovhost deploy openre` (OpenVibe.Host strategy release-layout since the 2026-09-27 cutover; deploy/scripts/deploy.sh is the wrapper and keeps workers/status/prune): API + coordinator on 127.0.0.1:4500, RTMP ingest public on 0.0.0.0:1936 (ingest.openre.stream, DNS-only; reachable from outside since 2026-09-23), Live wired (OPENRE_URL, openre.session.* subscription), preflight all PASS, restore drill passed. No slot switched yet: every slot is still ingested by Live until the per-slot cutover (docs/cutover.md phase B).",
+ "updated": "2026-09-28",
+ "features": [
+ "stream definitions owned by a Network subject, with typed references",
+ "ingest keys hashed at rest, shown once, rotation with grace",
+ "RTMP ingest workers with generations, leases and drains that survive API deploys",
+ "RTMP/RTMPS/SRT restream with output health and a circuit breaker",
+ "SSRF-checked destinations with sealed keys",
+ "recording requests to OpenVibe.Media",
+ "HTTP-FLV playback descriptors",
+ "outbox events openre.session.*, openre.output.*, openre.recording.requested, openre.key.rotated",
+ "migration from Live without importing old keys",
+ "restore-drill read-only mode",
+ "per-actor limits (openvibe-sdk/limits)"
+ ]
}
}