openvibers/openvibe.vip Commits
VIP: the key provider is openvibe-sdk/auth createNetworkKeys (SDK 0.40.0) (#37)
Changes
@@@ -460,6 +460,6 @@ Part of the [OpenVibe network](https://openvibe.network). Built in the open by [<!-- versions:start -->- openvibe-contracts: v0.129.0--- openvibe-sdk: v0.38.0++- openvibe-sdk: v0.40.0- openvibe-shared: v3.0.0<!-- versions:end -->@@@ -5,7 +5,7 @@"deployed": true,"deployment": "Deployed internally, not launched: deployed with `sudo ovhost deploy vip`, /opt/openvibe.vip, unit openvibe-vip, 127.0.0.1:4620 loopback only; https://openvibe.vip still serves the OpenVibe.Sites placeholder. Principal vip; three Events subscriptions for Billing's events (secret rotated 2026-09-24); checkout closed (VIP_CHECKOUT_PROVIDERS empty). On 2026-09-24 production held the Live import: 44 creators, 43 plans (one per creator), 0 memberships while Billing runs in shadow.","contracts": "openvibe-contracts v0.129.0",-- "sdk": "openvibe-sdk v0.38.0",++ "sdk": "openvibe-sdk v0.40.0","shared": "openvibe-shared v3.0.0","plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)","roadmap": "Wave 10",@@@ -14,7 +14,7 @@"express": "^4.21.2","iovalkey": "^0.4.0","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.129.0",-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.38.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.40.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v3.0.0","pg": "^8.23.0"},@@@ -859,9 +859,9 @@}},"node_modules/openvibe-sdk": {-- "version": "0.38.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.38.0",-- "integrity": "sha512-/Z+1kB5l4Hff0E1O1Zo+DcXaZX93wrmISOPP75sB7NqVUZVeKuJgjYu6vBwG8ufJjlk0vD3ZJ0ntBLI/3IKdpg==",++ "version": "0.40.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.40.0",++ "integrity": "sha512-fYX0zijJZDdwjXJGwroLDlb/wniY6NXVtgdPVJJHNgcdRmaAydpr6SKuxcLffmkTquyZQptwChsCvo+D6Wx+IA==","license": "MIT","engines": {"node": ">=20"@@@ -31,7 +31,7 @@"express": "^4.21.2","iovalkey": "^0.4.0","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.129.0",-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.38.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.40.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v3.0.0","pg": "^8.23.0"},@@@ -49,7 +49,7 @@ function createApiAuth({ config, keys, userAuth }) {const header = String(req.headers.authorization || '');if (!header.startsWith('Bearer ')) return { principal: ANON };const token = header.slice(7).trim();-- if (!keys.ready()) return { error: [503, 'identity.unavailable', 'the Network signing key is not loaded yet'] };++ if (!keys.loaded()) return { error: [503, 'identity.unavailable', 'the Network signing key is not loaded yet'] };const payload = decodePayload(token);if (payload && typeof payload.sub === 'string' && PRINCIPAL_SUB.test(payload.sub)) {let r;@@@ -43,7 +43,7 @@ async function start() {const app = await createApp({ config });const { domain, keys, outbox } = app.locals;-- keys.start();++ keys.start().catch(() => { /* logged by the JWKS client */ });const timers = [];if (config.jobs.enabled) {@@@ -3,35 +3,24 @@/*** OpenVibe.Network client.*-- * keys the Network's RS256 signing keys through openvibe-sdk/auth: one process-wide JWKS client per URL keeps-- * them fresh, serves the last good keys through a Network outage, backs off, and refetches at once when a-- * token names a key it does not have (a rotation). OV_NETWORK_PUBLIC_KEY pins one key instead. Service++ * keys the Network's RS256 signing keys: openvibe-sdk/auth createNetworkKeys. The JWKS is retried every 30 s++ * until it loads, then kept fresh; the last good keys are served through a Network outage, and a token that++ * names a key the client does not have refetches at once (a rotation). OV_NETWORK_PUBLIC_KEY pins one key++ * instead. Service* tokens (audience openvibe.vip) and the browser's Network user JWT are both verified offline.*/--const crypto = require('crypto');const contracts = require('openvibe-contracts');--const { jwksClient, verifyUserToken, verifyServiceToken } = require('openvibe-sdk/auth');++const { createNetworkKeys, verifyUserToken, verifyServiceToken } = require('openvibe-sdk/auth');const { OpenVibeAuthClient } = require('openvibe-shared/auth-client');++/** keys.verifyOptions (spread into the verifiers), keys.loaded(), keys.start(), keys.stop(), keys.refresh(). */function createKeyProvider(config, { fetchImpl = globalThis.fetch, log = console } = {}) {-- const url = `${config.network.internalUrl}/api/.well-known/jwks`;-- const pinned = config.network.publicKey ? crypto.createPublicKey(config.network.publicKey) : null;-- const client = pinned ? null : jwksClient(url, { fetch: fetchImpl, log });-- /** Where a verification takes its keys from (openvibe-sdk/auth options). */-- const source = pinned ? { publicKey: pinned } : { jwks: url, fetch: fetchImpl };-- return {-- source,-- ready: () => (pinned ? true : client.status().ready),-- /** Fetch the keys now (boot, tests); true when there is a key to verify with. */-- async load() { if (client) await client.refresh().catch(() => {}); return pinned ? true : client.status().ready; },-- start() { if (client) client.start(); },-- stop() { if (client) client.stop(); },-- };++ return createNetworkKeys({ network: config.network.internalUrl, publicKey: config.network.publicKey || null, fetch: fetchImpl, log });}/** A service principal's token (audience openvibe.vip): contracts' result { ok, code, reason, claims }. */async function verifyService(config, keys, token) {-- return await verifyServiceToken(token, { ...keys.source, issuer: config.network.issuer, audience: config.audience, contracts });++ return await verifyServiceToken(token, { ...keys.verifyOptions, issuer: config.network.issuer, audience: config.audience, contracts });}/**@@@ -53,7 +42,7 @@ function createUserAuth(config, keys) {*/async function verify(token) {if (!token) return null;-- try { return await verifyUserToken(token, { ...keys.source, issuer: config.network.issuer }); } catch { return null; }++ try { return await verifyUserToken(token, { ...keys.verifyOptions, issuer: config.network.issuer }); } catch { return null; }}return { client, verify };}@@@ -33,7 +33,7 @@ function createVipReadiness({ db, keys, config, outbox, now, release = null, fet{ name: 'valkey', required: false, check: async () => (valkey ? valkey.ready() : { skipped: 'VALKEY_URL not set: per-actor limits count in this process only' }) },{name: 'network_jwks', required: false,-- check: () => (keys.ready() ? true : 'Network signing key not loaded yet: tokens cannot be verified'),++ check: () => (keys.loaded() ? true : 'Network signing key not loaded yet: tokens cannot be verified'),},{name: 'billing', required: false, cacheMs: 15_000, timeoutMs: 2500,
@@ -460,6 +460,6 @@ Part of the [OpenVibe network](https://openvibe.network). Built in the open by [
@@ -460,6 +460,6 @@ Part of the [OpenVibe network](https://openvibe.network). Built in the open by [
<!-- versions:start -->
<!-- versions:start -->
- openvibe-contracts: v0.129.0
- openvibe-contracts: v0.129.0
-- openvibe-sdk: v0.38.0
+- openvibe-sdk: v0.40.0
- openvibe-shared: v3.0.0
- openvibe-shared: v3.0.0
<!-- versions:end -->
<!-- versions:end -->
@@ -5,7 +5,7 @@
@@ -5,7 +5,7 @@
"deployed": true,
"deployed": true,
"deployment": "Deployed internally, not launched: deployed with `sudo ovhost deploy vip`, /opt/openvibe.vip, unit openvibe-vip, 127.0.0.1:4620 loopback only; https://openvibe.vip still serves the OpenVibe.Sites placeholder. Principal vip; three Events subscriptions for Billing's events (secret rotated 2026-09-24); checkout closed (VIP_CHECKOUT_PROVIDERS empty). On 2026-09-24 production held the Live import: 44 creators, 43 plans (one per creator), 0 memberships while Billing runs in shadow.",
"deployment": "Deployed internally, not launched: deployed with `sudo ovhost deploy vip`, /opt/openvibe.vip, unit openvibe-vip, 127.0.0.1:4620 loopback only; https://openvibe.vip still serves the OpenVibe.Sites placeholder. Principal vip; three Events subscriptions for Billing's events (secret rotated 2026-09-24); checkout closed (VIP_CHECKOUT_PROVIDERS empty). On 2026-09-24 production held the Live import: 44 creators, 43 plans (one per creator), 0 memberships while Billing runs in shadow.",
"contracts": "openvibe-contracts v0.129.0",
"contracts": "openvibe-contracts v0.129.0",
- "sdk": "openvibe-sdk v0.38.0",
+ "sdk": "openvibe-sdk v0.40.0",
"shared": "openvibe-shared v3.0.0",
"shared": "openvibe-shared v3.0.0",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
"roadmap": "Wave 10",
"roadmap": "Wave 10",
@@ -14,7 +14,7 @@
@@ -14,7 +14,7 @@
"express": "^4.21.2",
"express": "^4.21.2",
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.129.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.129.0",
- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.38.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.40.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v3.0.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v3.0.0",
"pg": "^8.23.0"
"pg": "^8.23.0"
},
},
@@ -859,9 +859,9 @@
@@ -859,9 +859,9 @@
}
}
},
},
"node_modules/openvibe-sdk": {
"node_modules/openvibe-sdk": {
- "version": "0.38.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.38.0",
- "integrity": "sha512-/Z+1kB5l4Hff0E1O1Zo+DcXaZX93wrmISOPP75sB7NqVUZVeKuJgjYu6vBwG8ufJjlk0vD3ZJ0ntBLI/3IKdpg==",
+ "version": "0.40.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.40.0",
+ "integrity": "sha512-fYX0zijJZDdwjXJGwroLDlb/wniY6NXVtgdPVJJHNgcdRmaAydpr6SKuxcLffmkTquyZQptwChsCvo+D6Wx+IA==",
"license": "MIT",
"license": "MIT",
"engines": {
"engines": {
"node": ">=20"
"node": ">=20"
@@ -31,7 +31,7 @@
@@ -31,7 +31,7 @@
"express": "^4.21.2",
"express": "^4.21.2",
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.129.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.129.0",
- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.38.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.40.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v3.0.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v3.0.0",
"pg": "^8.23.0"
"pg": "^8.23.0"
},
},
@@ -49,7 +49,7 @@ function createApiAuth({ config, keys, userAuth }) {
@@ -49,7 +49,7 @@ function createApiAuth({ config, keys, userAuth }) {
const header = String(req.headers.authorization || '');
const header = String(req.headers.authorization || '');
if (!header.startsWith('Bearer ')) return { principal: ANON };
if (!header.startsWith('Bearer ')) return { principal: ANON };
const token = header.slice(7).trim();
const token = header.slice(7).trim();
- if (!keys.ready()) return { error: [503, 'identity.unavailable', 'the Network signing key is not loaded yet'] };
+ if (!keys.loaded()) return { error: [503, 'identity.unavailable', 'the Network signing key is not loaded yet'] };
const payload = decodePayload(token);
const payload = decodePayload(token);
if (payload && typeof payload.sub === 'string' && PRINCIPAL_SUB.test(payload.sub)) {
if (payload && typeof payload.sub === 'string' && PRINCIPAL_SUB.test(payload.sub)) {
let r;
let r;
@@ -43,7 +43,7 @@ async function start() {
@@ -43,7 +43,7 @@ async function start() {
const app = await createApp({ config });
const app = await createApp({ config });
const { domain, keys, outbox } = app.locals;
const { domain, keys, outbox } = app.locals;
- keys.start();
+ keys.start().catch(() => { /* logged by the JWKS client */ });
const timers = [];
const timers = [];
if (config.jobs.enabled) {
if (config.jobs.enabled) {
@@ -3,35 +3,24 @@
@@ -3,35 +3,24 @@
/**
/**
* OpenVibe.Network client.
* OpenVibe.Network client.
*
*
- * keys the Network's RS256 signing keys through openvibe-sdk/auth: one process-wide JWKS client per URL keeps
- * them fresh, serves the last good keys through a Network outage, backs off, and refetches at once when a
- * token names a key it does not have (a rotation). OV_NETWORK_PUBLIC_KEY pins one key instead. Service
+ * keys the Network's RS256 signing keys: openvibe-sdk/auth createNetworkKeys. The JWKS is retried every 30 s
+ * until it loads, then kept fresh; the last good keys are served through a Network outage, and a token that
+ * names a key the client does not have refetches at once (a rotation). OV_NETWORK_PUBLIC_KEY pins one key
+ * instead. Service
* tokens (audience openvibe.vip) and the browser's Network user JWT are both verified offline.
* tokens (audience openvibe.vip) and the browser's Network user JWT are both verified offline.
*/
*/
-const crypto = require('crypto');
const contracts = require('openvibe-contracts');
const contracts = require('openvibe-contracts');
-const { jwksClient, verifyUserToken, verifyServiceToken } = require('openvibe-sdk/auth');
+const { createNetworkKeys, verifyUserToken, verifyServiceToken } = require('openvibe-sdk/auth');
const { OpenVibeAuthClient } = require('openvibe-shared/auth-client');
const { OpenVibeAuthClient } = require('openvibe-shared/auth-client');
+/** keys.verifyOptions (spread into the verifiers), keys.loaded(), keys.start(), keys.stop(), keys.refresh(). */
function createKeyProvider(config, { fetchImpl = globalThis.fetch, log = console } = {}) {
function createKeyProvider(config, { fetchImpl = globalThis.fetch, log = console } = {}) {
- const url = `${config.network.internalUrl}/api/.well-known/jwks`;
- const pinned = config.network.publicKey ? crypto.createPublicKey(config.network.publicKey) : null;
- const client = pinned ? null : jwksClient(url, { fetch: fetchImpl, log });
- /** Where a verification takes its keys from (openvibe-sdk/auth options). */
- const source = pinned ? { publicKey: pinned } : { jwks: url, fetch: fetchImpl };
- return {
- source,
- ready: () => (pinned ? true : client.status().ready),
- /** Fetch the keys now (boot, tests); true when there is a key to verify with. */
- async load() { if (client) await client.refresh().catch(() => {}); return pinned ? true : client.status().ready; },
- start() { if (client) client.start(); },
- stop() { if (client) client.stop(); },
- };
+ return createNetworkKeys({ network: config.network.internalUrl, publicKey: config.network.publicKey || null, fetch: fetchImpl, log });
}
}
/** A service principal's token (audience openvibe.vip): contracts' result { ok, code, reason, claims }. */
/** A service principal's token (audience openvibe.vip): contracts' result { ok, code, reason, claims }. */
async function verifyService(config, keys, token) {
async function verifyService(config, keys, token) {
- return await verifyServiceToken(token, { ...keys.source, issuer: config.network.issuer, audience: config.audience, contracts });
+ return await verifyServiceToken(token, { ...keys.verifyOptions, issuer: config.network.issuer, audience: config.audience, contracts });
}
}
/**
/**
@@ -53,7 +42,7 @@ function createUserAuth(config, keys) {
@@ -53,7 +42,7 @@ function createUserAuth(config, keys) {
*/
*/
async function verify(token) {
async function verify(token) {
if (!token) return null;
if (!token) return null;
- try { return await verifyUserToken(token, { ...keys.source, issuer: config.network.issuer }); } catch { return null; }
+ try { return await verifyUserToken(token, { ...keys.verifyOptions, issuer: config.network.issuer }); } catch { return null; }
}
}
return { client, verify };
return { client, verify };
}
}
@@ -33,7 +33,7 @@ function createVipReadiness({ db, keys, config, outbox, now, release = null, fet
@@ -33,7 +33,7 @@ function createVipReadiness({ db, keys, config, outbox, now, release = null, fet
{ name: 'valkey', required: false, check: async () => (valkey ? valkey.ready() : { skipped: 'VALKEY_URL not set: per-actor limits count in this process only' }) },
{ name: 'valkey', required: false, check: async () => (valkey ? valkey.ready() : { skipped: 'VALKEY_URL not set: per-actor limits count in this process only' }) },
{
{
name: 'network_jwks', required: false,
name: 'network_jwks', required: false,
- check: () => (keys.ready() ? true : 'Network signing key not loaded yet: tokens cannot be verified'),
+ check: () => (keys.loaded() ? true : 'Network signing key not loaded yet: tokens cannot be verified'),
},
},
{
{
name: 'billing', required: false, cacheMs: 15_000, timeoutMs: 2500,
name: 'billing', required: false, cacheMs: 15_000, timeoutMs: 2500,