openvibers/openvibe.network Commits
Network: realtime tickets name openvibe.events, connect-src follows; contracts 0.111.0, shared 2.13.0 (plan T7) (#76)
Changes
@@@ -48,8 +48,8 @@ Identity and account service for the OpenVibe network. Manages user accounts, OA## Depends on--- `openvibe-contracts` v0.110.0 (registry manifests, capability checks), `openvibe-sdk` v0.28.0 (PostgreSQL-- and Valkey adapters, events, per-actor limits, universal telemetry), `openvibe-shared` v2.10.1, pinned by release tarball++- `openvibe-contracts` v0.111.0 (registry manifests, capability checks), `openvibe-sdk` v0.28.0 (PostgreSQL++ and Valkey adapters, events, per-actor limits, universal telemetry), `openvibe-shared` v2.13.0, pinned by release tarball- OpenVibe.Events (Network's outbox relay; the events it consumes), OpenVibe.AI (the `network.site_copy`workflow), OpenVibe.Blog (the changelog feed it proxies)- email (Resend), Discord and GitHub when their secrets are set ([Provider secrets](#provider-secrets))@@@ -345,7 +345,7 @@ Every notification `NotificationService.create()` stores is announced as **`netw- **Payload:** `notification_id`, `type`, `category`, `priority`, `service`, `created_at` and the recipient's `unread_count`. Never the title, message, link or sender.- **Nothing announced:** a muted category, a blocked sender or a go-live dedupe stores nothing, so it announces nothing. Guests and accounts without a `usr_` subject get no event.--The badge on other sites cannot use a cookie of events.openvibe.network, so it asks here for a **realtime ticket**:++The badge on other sites cannot use a cookie of openvibe.events, so it asks here for a **realtime ticket**:```POST /api/v1/realtime/ticket (Bearer Network JWT, or the ov_token cookie here; 60/min per IP)@@@ -356,7 +356,7 @@ It then opens `${stream_url}?topics=network.notification.*&ticket=…[&last_even- **The ticket** (`server/auth/realtime-ticket.js`, `identity.realtime-ticket-claims@1`) is an RS256 JWT: `iss <issuer>/realtime`, `sub <usr_>`, `aud [openvibe.events]`, `typ` and `purpose` `realtime`, 120 s, `jti rtk_…`. Events accepts each one once.- **Never a session:** its issuer, `typ` and audience each rule that out, and this session guard refuses it too.- **Not stored, logged or audited:** it grants the person only their own stream.--- **Refusals:** a guest gets 403 `realtime.guest`. `REALTIME_TICKETS=off` in `/etc/openvibe/network.env` answers 503 `realtime.disabled`, and every badge stays on polling. `OV_EVENTS_PUBLIC_URL` overrides the stream origin (default `https://events.openvibe.network`).++- **Refusals:** a guest gets 403 `realtime.guest`. `REALTIME_TICKETS=off` in `/etc/openvibe/network.env` answers 503 `realtime.disabled`, and every badge stays on polling. `OV_EVENTS_PUBLIC_URL` overrides the stream origin (default `https://openvibe.events`).- **Client:** openvibe-shared `notification-live.js`, on where a site sets `notificationsRealtime: true`.A digest (a daily or weekly summary instead of one alert per notification) is designed in [docs/notification-digest.md](docs/notification-digest.md) for a later release.@@@ -21,9 +21,9 @@"iovalkey": "^0.4.0","jsonwebtoken": "^9.0.0","multer": "^1.4.5-lts.1",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.110.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.111.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.28.0",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.10.1",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.13.0","pg": "^8.23.0","uuid": "^11.1.0","web-push": "^3.6.7"@@@ -1556,9 +1556,9 @@}},"node_modules/openvibe-contracts": {-- "version": "0.110.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.110.0",-- "integrity": "sha512-+65KnRBVOzPN+MKSdaKzbvop6CT/wr8ZrO9mrQrRDdprRDmL4+CCOnykKYFOiMWPtoYkXlYg+GXmwmOw7eBL/g==",++ "version": "0.111.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.111.0",++ "integrity": "sha512-3F+SPc1m/8fvA8Gs/1mSn6/3FgbMltStl63vHKBioc6pAC3pAJov5M2E4qA3SBGvGo1fYCvDjwqS0CExHvOBFg==","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -1601,9 +1601,9 @@}},"node_modules/openvibe-shared": {-- "version": "2.10.1",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.10.1",-- "integrity": "sha512-6fUOw22bSvxg0K33BefAyAkNRkETe9euLNT0CHs2mLIvzYR1q38KiHRUQIWj8FESbVANF0pXMojhBz5TeqpMtg==",++ "version": "2.13.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.13.0",++ "integrity": "sha512-0Lvo+bBWVGWREkp92tY4cGI32ZO124WTOjthDNTB96U161odl+npnYEWy3dV2IcgZC4HAX5DSzx4ThgNS2V/sA==","license": "MIT","dependencies": {"jsonwebtoken": "^9.0.0"@@@ -30,9 +30,9 @@"iovalkey": "^0.4.0","jsonwebtoken": "^9.0.0","multer": "^1.4.5-lts.1",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.110.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.111.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.28.0",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.10.1",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.13.0","pg": "^8.23.0","uuid": "^11.1.0","web-push": "^3.6.7"@@@ -33,7 +33,7 @@ const AREAS = [{ item: 'Media operations (jobs, tiers, repairs)', where: [{ kind: 'api', api: 'OpenVibe.Media /api/v1/jobs, /internal/*', note: 'no console yet' }, { kind: 'cli', cli: 'node scripts/h15-repair.js, object-drift-report.js (Media)' }], service: 'media' },] },{ area: 'Queues', items: [-- { item: 'Events: subscriptions, dead letters, replay', where: [{ kind: 'tab', tab: 'eventsops' }, { kind: 'url', url: 'https://events.openvibe.network/' }], service: 'events' },++ { item: 'Events: subscriptions, dead letters, replay', where: [{ kind: 'tab', tab: 'eventsops' }, { kind: 'url', url: 'https://openvibe.events/' }], service: 'events' },{ item: 'AI runs, providers and routes', where: [{ kind: 'url', url: 'https://ai.openvibe.services/' }, { kind: 'tab', tab: 'ai' }], service: 'ai' },] },{ area: 'Moderation, money and loyalty', items: [@@@ -7,7 +7,7 @@** The notification badge on every OpenVibe site listens for the signed-in person's* network.notification.created events on OpenVibe.Events' /realtime/stream. An EventSource cannot send-- * a header, and events.openvibe.network's cookies are third-party on every other site, so the badge asks++ * a header, and openvibe.events's cookies are third-party on every other site, so the badge asks* here (Bearer Network JWT, or the ov_token cookie on openvibe.network) for a ticket and opens* `${stream_url}?topics=network.notification.*&ticket=…`. A ticket opens one stream: every reconnect* asks for a new one and resumes with last_event_id.@@@ -29,9 +29,9 @@ const jwt = require('jsonwebtoken');const TTL_S = 120;const TOPICS = Object.freeze(['network.notification.*']);--const DEFAULT_STREAM_URL = 'https://events.openvibe.network/realtime/stream';++const DEFAULT_STREAM_URL = 'https://openvibe.events/realtime/stream';--/** The stream URL browsers use: OV_EVENTS_PUBLIC_URL (an origin, or the stream URL itself) or events.openvibe.network. */++/** The stream URL browsers use: OV_EVENTS_PUBLIC_URL (an origin, or the stream URL itself) or openvibe.events. */function streamUrlFrom(value) {const v = String(value || '').trim().replace(/\/+$/, '');if (!v) return DEFAULT_STREAM_URL;@@@ -152,7 +152,7 @@ app.use(helmet({"https://openvibe.blog","https://cloudflareinsights.com", // Cloudflare Web Analytics reports here (see script-src)// Release notifications: release-watch's EventSource on the Events realtime stream (openvibe-shared 1.17)-- "https://events.openvibe.network",++ "https://openvibe.events",],frameSrc: ["'none'"],scriptSrcAttr: ["'unsafe-inline'"],@@@ -133,7 +133,7 @@ const checkEnvelope = (env, subject) => {assert.strictEqual(r.status, 200, JSON.stringify(r.body));assert.strictEqual(r.headers.get('cache-control'), 'no-store');assert.deepStrictEqual(Object.keys(r.body).sort(), ['expires_at', 'expires_in', 'stream_url', 'subject', 'ticket', 'topics']);-- assert.deepStrictEqual([r.body.expires_in, r.body.stream_url, r.body.topics, r.body.subject], [120, 'https://events.openvibe.network/realtime/stream', ['network.notification.*'], ALICE]);++ assert.deepStrictEqual([r.body.expires_in, r.body.stream_url, r.body.topics, r.body.subject], [120, 'https://openvibe.events/realtime/stream', ['network.notification.*'], ALICE]);try { const v = contracts.validate('network.realtime-ticket-result@1', r.body); assert.ok(v.valid, JSON.stringify(v.errors)); } catch (err) { if (!/unknown contract/.test(err.message)) throw err; }// What Events checks (OpenVibe.Events server/auth.js verifyRealtimeTicket).const claims = jwt.verify(r.body.ticket, publicKey, { algorithms: ['RS256'], issuer: `${ISSUER}/realtime`, audience: 'openvibe.events' });@@@ -166,7 +166,7 @@ const checkEnvelope = (env, subject) => {assert.match(r.body.subject, /^usr_/);assert.strictEqual((await db.prepare('SELECT subject_id FROM users WHERE id = 4').get()).subject_id, r.body.subject);// The stream URL: the default, an operator's origin, or the default for anything odd.-- assert.strictEqual(ticketMod.streamUrlFrom(''), 'https://events.openvibe.network/realtime/stream');++ assert.strictEqual(ticketMod.streamUrlFrom(''), 'https://openvibe.events/realtime/stream');assert.strictEqual(ticketMod.streamUrlFrom('https://events.example.test/'), 'https://events.example.test/realtime/stream');assert.strictEqual(ticketMod.streamUrlFrom('https://events.example.test/realtime/stream'), 'https://events.example.test/realtime/stream');for (const bad of ['javascript:alert(1)', 'https://x.test/?a=1', 'not a url', 'https://u:[email protected]']) assert.strictEqual(ticketMod.streamUrlFrom(bad), ticketMod.DEFAULT_STREAM_URL, bad);
@@ -48,8 +48,8 @@ Identity and account service for the OpenVibe network. Manages user accounts, OA
@@ -48,8 +48,8 @@ Identity and account service for the OpenVibe network. Manages user accounts, OA
## Depends on
## Depends on
-- `openvibe-contracts` v0.110.0 (registry manifests, capability checks), `openvibe-sdk` v0.28.0 (PostgreSQL
- and Valkey adapters, events, per-actor limits, universal telemetry), `openvibe-shared` v2.10.1, pinned by release tarball
+- `openvibe-contracts` v0.111.0 (registry manifests, capability checks), `openvibe-sdk` v0.28.0 (PostgreSQL
+ and Valkey adapters, events, per-actor limits, universal telemetry), `openvibe-shared` v2.13.0, pinned by release tarball
- OpenVibe.Events (Network's outbox relay; the events it consumes), OpenVibe.AI (the `network.site_copy`
- OpenVibe.Events (Network's outbox relay; the events it consumes), OpenVibe.AI (the `network.site_copy`
workflow), OpenVibe.Blog (the changelog feed it proxies)
workflow), OpenVibe.Blog (the changelog feed it proxies)
- email (Resend), Discord and GitHub when their secrets are set ([Provider secrets](#provider-secrets))
- email (Resend), Discord and GitHub when their secrets are set ([Provider secrets](#provider-secrets))
@@ -345,7 +345,7 @@ Every notification `NotificationService.create()` stores is announced as **`netw
@@ -345,7 +345,7 @@ Every notification `NotificationService.create()` stores is announced as **`netw
- **Payload:** `notification_id`, `type`, `category`, `priority`, `service`, `created_at` and the recipient's `unread_count`. Never the title, message, link or sender.
- **Payload:** `notification_id`, `type`, `category`, `priority`, `service`, `created_at` and the recipient's `unread_count`. Never the title, message, link or sender.
- **Nothing announced:** a muted category, a blocked sender or a go-live dedupe stores nothing, so it announces nothing. Guests and accounts without a `usr_` subject get no event.
- **Nothing announced:** a muted category, a blocked sender or a go-live dedupe stores nothing, so it announces nothing. Guests and accounts without a `usr_` subject get no event.
-The badge on other sites cannot use a cookie of events.openvibe.network, so it asks here for a **realtime ticket**:
+The badge on other sites cannot use a cookie of openvibe.events, so it asks here for a **realtime ticket**:
```
```
POST /api/v1/realtime/ticket (Bearer Network JWT, or the ov_token cookie here; 60/min per IP)
POST /api/v1/realtime/ticket (Bearer Network JWT, or the ov_token cookie here; 60/min per IP)
@@ -356,7 +356,7 @@ It then opens `${stream_url}?topics=network.notification.*&ticket=…[&last_even
@@ -356,7 +356,7 @@ It then opens `${stream_url}?topics=network.notification.*&ticket=…[&last_even
- **The ticket** (`server/auth/realtime-ticket.js`, `identity.realtime-ticket-claims@1`) is an RS256 JWT: `iss <issuer>/realtime`, `sub <usr_>`, `aud [openvibe.events]`, `typ` and `purpose` `realtime`, 120 s, `jti rtk_…`. Events accepts each one once.
- **The ticket** (`server/auth/realtime-ticket.js`, `identity.realtime-ticket-claims@1`) is an RS256 JWT: `iss <issuer>/realtime`, `sub <usr_>`, `aud [openvibe.events]`, `typ` and `purpose` `realtime`, 120 s, `jti rtk_…`. Events accepts each one once.
- **Never a session:** its issuer, `typ` and audience each rule that out, and this session guard refuses it too.
- **Never a session:** its issuer, `typ` and audience each rule that out, and this session guard refuses it too.
- **Not stored, logged or audited:** it grants the person only their own stream.
- **Not stored, logged or audited:** it grants the person only their own stream.
-- **Refusals:** a guest gets 403 `realtime.guest`. `REALTIME_TICKETS=off` in `/etc/openvibe/network.env` answers 503 `realtime.disabled`, and every badge stays on polling. `OV_EVENTS_PUBLIC_URL` overrides the stream origin (default `https://events.openvibe.network`).
+- **Refusals:** a guest gets 403 `realtime.guest`. `REALTIME_TICKETS=off` in `/etc/openvibe/network.env` answers 503 `realtime.disabled`, and every badge stays on polling. `OV_EVENTS_PUBLIC_URL` overrides the stream origin (default `https://openvibe.events`).
- **Client:** openvibe-shared `notification-live.js`, on where a site sets `notificationsRealtime: true`.
- **Client:** openvibe-shared `notification-live.js`, on where a site sets `notificationsRealtime: true`.
A digest (a daily or weekly summary instead of one alert per notification) is designed in [docs/notification-digest.md](docs/notification-digest.md) for a later release.
A digest (a daily or weekly summary instead of one alert per notification) is designed in [docs/notification-digest.md](docs/notification-digest.md) for a later release.
@@ -21,9 +21,9 @@
@@ -21,9 +21,9 @@
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
"jsonwebtoken": "^9.0.0",
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
"multer": "^1.4.5-lts.1",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.110.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.111.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.28.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.28.0",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.10.1",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.13.0",
"pg": "^8.23.0",
"pg": "^8.23.0",
"uuid": "^11.1.0",
"uuid": "^11.1.0",
"web-push": "^3.6.7"
"web-push": "^3.6.7"
@@ -1556,9 +1556,9 @@
@@ -1556,9 +1556,9 @@
}
}
},
},
"node_modules/openvibe-contracts": {
"node_modules/openvibe-contracts": {
- "version": "0.110.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.110.0",
- "integrity": "sha512-+65KnRBVOzPN+MKSdaKzbvop6CT/wr8ZrO9mrQrRDdprRDmL4+CCOnykKYFOiMWPtoYkXlYg+GXmwmOw7eBL/g==",
+ "version": "0.111.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.111.0",
+ "integrity": "sha512-3F+SPc1m/8fvA8Gs/1mSn6/3FgbMltStl63vHKBioc6pAC3pAJov5M2E4qA3SBGvGo1fYCvDjwqS0CExHvOBFg==",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -1601,9 +1601,9 @@
@@ -1601,9 +1601,9 @@
}
}
},
},
"node_modules/openvibe-shared": {
"node_modules/openvibe-shared": {
- "version": "2.10.1",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.10.1",
- "integrity": "sha512-6fUOw22bSvxg0K33BefAyAkNRkETe9euLNT0CHs2mLIvzYR1q38KiHRUQIWj8FESbVANF0pXMojhBz5TeqpMtg==",
+ "version": "2.13.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.13.0",
+ "integrity": "sha512-0Lvo+bBWVGWREkp92tY4cGI32ZO124WTOjthDNTB96U161odl+npnYEWy3dV2IcgZC4HAX5DSzx4ThgNS2V/sA==",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
"jsonwebtoken": "^9.0.0"
"jsonwebtoken": "^9.0.0"
@@ -30,9 +30,9 @@
@@ -30,9 +30,9 @@
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
"jsonwebtoken": "^9.0.0",
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
"multer": "^1.4.5-lts.1",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.110.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.111.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.28.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.28.0",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.10.1",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.13.0",
"pg": "^8.23.0",
"pg": "^8.23.0",
"uuid": "^11.1.0",
"uuid": "^11.1.0",
"web-push": "^3.6.7"
"web-push": "^3.6.7"
@@ -33,7 +33,7 @@ const AREAS = [
@@ -33,7 +33,7 @@ const AREAS = [
{ item: 'Media operations (jobs, tiers, repairs)', where: [{ kind: 'api', api: 'OpenVibe.Media /api/v1/jobs, /internal/*', note: 'no console yet' }, { kind: 'cli', cli: 'node scripts/h15-repair.js, object-drift-report.js (Media)' }], service: 'media' },
{ item: 'Media operations (jobs, tiers, repairs)', where: [{ kind: 'api', api: 'OpenVibe.Media /api/v1/jobs, /internal/*', note: 'no console yet' }, { kind: 'cli', cli: 'node scripts/h15-repair.js, object-drift-report.js (Media)' }], service: 'media' },
] },
] },
{ area: 'Queues', items: [
{ area: 'Queues', items: [
- { item: 'Events: subscriptions, dead letters, replay', where: [{ kind: 'tab', tab: 'eventsops' }, { kind: 'url', url: 'https://events.openvibe.network/' }], service: 'events' },
+ { item: 'Events: subscriptions, dead letters, replay', where: [{ kind: 'tab', tab: 'eventsops' }, { kind: 'url', url: 'https://openvibe.events/' }], service: 'events' },
{ item: 'AI runs, providers and routes', where: [{ kind: 'url', url: 'https://ai.openvibe.services/' }, { kind: 'tab', tab: 'ai' }], service: 'ai' },
{ item: 'AI runs, providers and routes', where: [{ kind: 'url', url: 'https://ai.openvibe.services/' }, { kind: 'tab', tab: 'ai' }], service: 'ai' },
] },
] },
{ area: 'Moderation, money and loyalty', items: [
{ area: 'Moderation, money and loyalty', items: [
@@ -7,7 +7,7 @@
@@ -7,7 +7,7 @@
*
*
* The notification badge on every OpenVibe site listens for the signed-in person's
* The notification badge on every OpenVibe site listens for the signed-in person's
* network.notification.created events on OpenVibe.Events' /realtime/stream. An EventSource cannot send
* network.notification.created events on OpenVibe.Events' /realtime/stream. An EventSource cannot send
- * a header, and events.openvibe.network's cookies are third-party on every other site, so the badge asks
+ * a header, and openvibe.events's cookies are third-party on every other site, so the badge asks
* here (Bearer Network JWT, or the ov_token cookie on openvibe.network) for a ticket and opens
* here (Bearer Network JWT, or the ov_token cookie on openvibe.network) for a ticket and opens
* `${stream_url}?topics=network.notification.*&ticket=…`. A ticket opens one stream: every reconnect
* `${stream_url}?topics=network.notification.*&ticket=…`. A ticket opens one stream: every reconnect
* asks for a new one and resumes with last_event_id.
* asks for a new one and resumes with last_event_id.
@@ -29,9 +29,9 @@ const jwt = require('jsonwebtoken');
@@ -29,9 +29,9 @@ const jwt = require('jsonwebtoken');
const TTL_S = 120;
const TTL_S = 120;
const TOPICS = Object.freeze(['network.notification.*']);
const TOPICS = Object.freeze(['network.notification.*']);
-const DEFAULT_STREAM_URL = 'https://events.openvibe.network/realtime/stream';
+const DEFAULT_STREAM_URL = 'https://openvibe.events/realtime/stream';
-/** The stream URL browsers use: OV_EVENTS_PUBLIC_URL (an origin, or the stream URL itself) or events.openvibe.network. */
+/** The stream URL browsers use: OV_EVENTS_PUBLIC_URL (an origin, or the stream URL itself) or openvibe.events. */
function streamUrlFrom(value) {
function streamUrlFrom(value) {
const v = String(value || '').trim().replace(/\/+$/, '');
const v = String(value || '').trim().replace(/\/+$/, '');
if (!v) return DEFAULT_STREAM_URL;
if (!v) return DEFAULT_STREAM_URL;
@@ -152,7 +152,7 @@ app.use(helmet({
@@ -152,7 +152,7 @@ app.use(helmet({
"https://openvibe.blog",
"https://openvibe.blog",
"https://cloudflareinsights.com", // Cloudflare Web Analytics reports here (see script-src)
"https://cloudflareinsights.com", // Cloudflare Web Analytics reports here (see script-src)
// Release notifications: release-watch's EventSource on the Events realtime stream (openvibe-shared 1.17)
// Release notifications: release-watch's EventSource on the Events realtime stream (openvibe-shared 1.17)
- "https://events.openvibe.network",
+ "https://openvibe.events",
],
],
frameSrc: ["'none'"],
frameSrc: ["'none'"],
scriptSrcAttr: ["'unsafe-inline'"],
scriptSrcAttr: ["'unsafe-inline'"],
@@ -133,7 +133,7 @@ const checkEnvelope = (env, subject) => {
@@ -133,7 +133,7 @@ const checkEnvelope = (env, subject) => {
assert.strictEqual(r.status, 200, JSON.stringify(r.body));
assert.strictEqual(r.status, 200, JSON.stringify(r.body));
assert.strictEqual(r.headers.get('cache-control'), 'no-store');
assert.strictEqual(r.headers.get('cache-control'), 'no-store');
assert.deepStrictEqual(Object.keys(r.body).sort(), ['expires_at', 'expires_in', 'stream_url', 'subject', 'ticket', 'topics']);
assert.deepStrictEqual(Object.keys(r.body).sort(), ['expires_at', 'expires_in', 'stream_url', 'subject', 'ticket', 'topics']);
- assert.deepStrictEqual([r.body.expires_in, r.body.stream_url, r.body.topics, r.body.subject], [120, 'https://events.openvibe.network/realtime/stream', ['network.notification.*'], ALICE]);
+ assert.deepStrictEqual([r.body.expires_in, r.body.stream_url, r.body.topics, r.body.subject], [120, 'https://openvibe.events/realtime/stream', ['network.notification.*'], ALICE]);
try { const v = contracts.validate('network.realtime-ticket-result@1', r.body); assert.ok(v.valid, JSON.stringify(v.errors)); } catch (err) { if (!/unknown contract/.test(err.message)) throw err; }
try { const v = contracts.validate('network.realtime-ticket-result@1', r.body); assert.ok(v.valid, JSON.stringify(v.errors)); } catch (err) { if (!/unknown contract/.test(err.message)) throw err; }
// What Events checks (OpenVibe.Events server/auth.js verifyRealtimeTicket).
// What Events checks (OpenVibe.Events server/auth.js verifyRealtimeTicket).
const claims = jwt.verify(r.body.ticket, publicKey, { algorithms: ['RS256'], issuer: `${ISSUER}/realtime`, audience: 'openvibe.events' });
const claims = jwt.verify(r.body.ticket, publicKey, { algorithms: ['RS256'], issuer: `${ISSUER}/realtime`, audience: 'openvibe.events' });
@@ -166,7 +166,7 @@ const checkEnvelope = (env, subject) => {
@@ -166,7 +166,7 @@ const checkEnvelope = (env, subject) => {
assert.match(r.body.subject, /^usr_/);
assert.match(r.body.subject, /^usr_/);
assert.strictEqual((await db.prepare('SELECT subject_id FROM users WHERE id = 4').get()).subject_id, r.body.subject);
assert.strictEqual((await db.prepare('SELECT subject_id FROM users WHERE id = 4').get()).subject_id, r.body.subject);
// The stream URL: the default, an operator's origin, or the default for anything odd.
// The stream URL: the default, an operator's origin, or the default for anything odd.
- assert.strictEqual(ticketMod.streamUrlFrom(''), 'https://events.openvibe.network/realtime/stream');
+ assert.strictEqual(ticketMod.streamUrlFrom(''), 'https://openvibe.events/realtime/stream');
assert.strictEqual(ticketMod.streamUrlFrom('https://events.example.test/'), 'https://events.example.test/realtime/stream');
assert.strictEqual(ticketMod.streamUrlFrom('https://events.example.test/'), 'https://events.example.test/realtime/stream');
assert.strictEqual(ticketMod.streamUrlFrom('https://events.example.test/realtime/stream'), 'https://events.example.test/realtime/stream');
assert.strictEqual(ticketMod.streamUrlFrom('https://events.example.test/realtime/stream'), 'https://events.example.test/realtime/stream');
for (const bad of ['javascript:alert(1)', 'https://x.test/?a=1', 'not a url', 'https://u:[email protected]']) assert.strictEqual(ticketMod.streamUrlFrom(bad), ticketMod.DEFAULT_STREAM_URL, bad);
for (const bad of ['javascript:alert(1)', 'https://x.test/?a=1', 'not a url', 'https://u:[email protected]']) assert.strictEqual(ticketMod.streamUrlFrom(bad), ticketMod.DEFAULT_STREAM_URL, bad);