openvibers/openvibe.host Commits
openvibe-shared v1.5.0 (Track R release manifest), openvibe-contracts v0.32.0, openvibe-sdk v0.5.0
Changes
@@@ -46,10 +46,10 @@ Stage A adds a few safety rules of its own:## Depends on--- OpenVibe.Contracts (`openvibe-contracts` v0.24.0; `host.*` capabilities and the `host` manifest are released there and unchanged in v0.30.1): service manifests (vhost rendering, snapshots, the first-party domain list), ids, problem+json, service-token verification, capability checks.++- OpenVibe.Contracts (`openvibe-contracts` v0.32.0; `host.*` capabilities and the `host` manifest were released in v0.24.0, and v0.32.0 adds the takedown routes to `host.site.manage`): service manifests (vhost rendering, snapshots, the first-party domain list), ids, problem+json, service-token verification, capability checks.- OpenVibe.Network (Stage B): SSO for the dashboard, the JWKS that verifies user and service tokens, client-credentials tokens for the outbox relay.--- OpenVibe.Events (Stage B): `host.*` events through the `openvibe-sdk` v0.2.2 transactional outbox.--- `openvibe-shared` v1.3.0 (Stage B): shared chrome, legal pages, `/release.json`, `/metrics`, `/api/ready`.++- OpenVibe.Events (Stage B): `host.*` events through the `openvibe-sdk` v0.5.0 transactional outbox.++- `openvibe-shared` v1.5.0 (Stage B): shared chrome, legal pages, `/release.json`, `/metrics`, `/api/ready`.- OpenVibe.Media: not yet. The roadmap stores artifacts "through Media where practical"; Stage B keeps them on local disk for now (see [Not done yet](#not-done-yet-stage-b)).## Stages@@@ -245,7 +245,7 @@ People present their Network user JWT as a Bearer token. Services and apps preseErrors are RFC 9457 problems (`application/problem+json`, with the legacy `error` field). A refused upload answers 413/422 with `deploy_id` and the log lines; `503 upload.busy` (with `Retry-After`) when `HOST_MAX_CONCURRENT_UPLOADS` uploads are already being validated; `507 storage.host_full` while the disk has less than `HOST_MIN_FREE_BYTES` free. Network staff (`role: admin`) can read every project, site, deploy and log, delete sites, projects and domains, set quotas, and **take a site or project down** (451 on every host, content kept for review, members see the reason and cannot publish or delete around it); they cannot publish into a tenant's site.--The three capability ids and the service manifest are released in `openvibe-contracts` (since v0.24.0, identical in v0.30.1), and the CI contract check is blocking. [`docs/capabilities-proposal/`](docs/capabilities-proposal/) mirrors them; `host.site.manage` there also lists the four takedown routes, which the next Contracts release should add (see [docs/launch.md](docs/launch.md#the-window-in-this-order)).++The three capability ids and the service manifest are released in `openvibe-contracts` (since v0.24.0; v0.32.0 adds the takedown routes to `host.site.manage`), and the CI contract check is blocking. [`docs/capabilities-proposal/`](docs/capabilities-proposal/) mirrors them; `host.site.manage` there also lists the four takedown routes, which the next Contracts release should add (see [docs/launch.md](docs/launch.md#the-window-in-this-order)).### Activation and rollback@@@ -253,7 +253,7 @@ Activation is one SQLite transaction: a compare-and-set on the site's `active_de### Events--Through the `openvibe-sdk` v0.2.2 transactional outbox (`event_outbox`), inside the transaction that makes the change:++Through the `openvibe-sdk` v0.5.0 transactional outbox (`event_outbox`), inside the transaction that makes the change:| Event | When | Payload ||---|---|---|@@@ -266,7 +266,7 @@ The relay publishes with Host's service token (`events.event.publish`, audience### Dashboard--Server-rendered pages with the shared chrome (`openvibe-shared` v1.3.0: navbar and theme loader from the Network, `<noscript>` navigation, SSR footer, app icon, legal pages). Every action is a plain form, so it works without JavaScript: projects, quotas and usage, members, sites, folder or archive upload, deploys with activate/rollback/delete, activation history, upload logs and file lists, domains with their DNS records and a "check DNS now" button. Pages are `private, no-store`, `noindex`, `frame-ancestors 'none'`.++Server-rendered pages with the shared chrome (`openvibe-shared` v1.5.0: navbar and theme loader from the Network, `<noscript>` navigation, SSR footer, app icon, legal pages). Every action is a plain form, so it works without JavaScript: projects, quotas and usage, members, sites, folder or archive upload, deploys with activate/rollback/delete, activation history, upload logs and file lists, domains with their DNS records and a "check DNS now" button. Pages are `private, no-store`, `noindex`, `frame-ancestors 'none'`.### Observability@@@ -277,7 +277,7 @@ Server-rendered pages with the shared chrome (`openvibe-shared` v1.3.0: navbar a- OpenVibe.Network OAuth client **`host`**, redirect `https://openvibe.host/auth/callback`, scope `profile theme`. The same client is the service principal `svc:host`.- Grant `[host, events.event.publish, openvibe.events]`.- Callers of Host get `[<client>, host.site.manage | host.deploy.create | host.domain.manage, openvibe.host]` as needed. None exist yet (Codes, the expected first caller, is not built).--- Released in `openvibe-contracts` v0.24.0 (CI contract check blocking). Next release: add the takedown routes to `host.site.manage.implementedBy` (docs/launch.md).++- Released in `openvibe-contracts` v0.24.0 (CI contract check blocking); v0.32.0 adds the takedown routes to `host.site.manage.implementedBy`.### Deploying Stage B (for the operator)@@@ -9,9 +9,9 @@"domain": "openvibe.host","tenantDomain": "*.openvibe.host","packages": [-- "openvibe-contracts v0.24.0 (host.* capabilities and manifest released; unchanged in v0.30.1)",-- "openvibe-sdk v0.2.2",-- "openvibe-shared v1.3.0"++ "openvibe-contracts v0.32.0 (host.* capabilities and manifest released in v0.24.0; v0.32.0 adds the takedown routes to host.site.manage)",++ "openvibe-sdk v0.5.0",++ "openvibe-shared v1.5.0"],"capabilities": ["host.site.manage",@@@ -38,6 +38,6 @@"placeholderInSites": true,"adoptedByServices": [],"updated": "2026-09-23",-- "capabilitiesNote": "released in openvibe-contracts v0.24.0 and identical in v0.30.1; the next release should add the four takedown routes to host.site.manage.implementedBy (docs/launch.md)",++ "capabilitiesNote": "released in openvibe-contracts v0.24.0; v0.32.0 adds the four takedown routes to host.site.manage.implementedBy","launchReady": true}@@@ -146,9 +146,8 @@ restart `openvibe-network`, poll `/api/ready`).**6. Host's own records.** In a follow-up Host commit, set README **Status**, `STATUS.json`(`"deployed"`, `"placeholderInSites": false`) and the Launch rule note to *launched on <date>*.--**Contracts** (no runtime effect, whenever the next release is cut): in--`manifests/capabilities/host.site.manage.json`, add the takedown routes and sentence from this--repository's `docs/capabilities-proposal/host.site.manage.json`:++**Contracts** (done in openvibe-contracts v0.32.0, which Host pins): `host.site.manage` lists the++takedown routes from this repository's `docs/capabilities-proposal/host.site.manage.json`:- `POST /api/v1/projects/:id/takedown`- `DELETE /api/v1/projects/:id/takedown`@@@ -16,9 +16,9 @@"dotenv": "^16.4.7","express": "^4.21.2","express-rate-limit": "^7.5.0",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.24.0",-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.2.2",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0"++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0"},"bin": {"ovhost": "bin/ovhost"@@@ -1412,9 +1412,9 @@}},"node_modules/openvibe-contracts": {-- "version": "0.24.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.24.0",-- "integrity": "sha512-8cG+TyI7DAEF1x2MAKlc6Y2AiKWArICYj0QNX4DAbTsOT+ENVTFN5AI35oByz3B1AvBX3nHUNFCK7IM1Zy/+QA==",++ "version": "0.32.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",++ "integrity": "sha512-ntN58SIG03YDxbhsiIzOM5GRH19jRMHZUYWI3xU2VwovExagF5DdwApLVfezsu84UTJ+V2Jfn+LWDc+Oz8ab+g==","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -1428,18 +1428,18 @@}},"node_modules/openvibe-sdk": {-- "version": "0.2.2",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.2.2",-- "integrity": "sha512-kDifUp3EcmnTWzElAmaCZnX659WErcnm4+qVEoAiYQWX/w/1p4TWvoHbUPsrX65TSROVR3tiyVkl801n/0yyCw==",++ "version": "0.5.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",++ "integrity": "sha512-QVJkR5iazQX0ZwIrDp2A88ZeYYBSXEmSzhpgURPUSnzezIM3jz0HBhVKowyhkvg2aBO61XNf77eb4zJGkMjZiw==","license": "MIT","engines": {"node": ">=20"}},"node_modules/openvibe-shared": {-- "version": "1.3.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0",-- "integrity": "sha512-w8yl/5+9eend8I2O/DzAJnEAL2N3GTUY7c7pzK+MLJuLAGD8FmJJQ9gwbgyrq70Snjt3ebrg3TlpORhRAp2kgA==",++ "version": "1.5.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0",++ "integrity": "sha512-cehnscuYMkJwuCYGcI6wPSswbUn0bhTyGAaD5feNCoO//2e9gyC7cgjkEcIOtM8DQF3yTuUNyXnOwckIeM4fUg==","license": "MIT","dependencies": {"jsonwebtoken": "^9.0.0"@@@ -31,9 +31,9 @@"dotenv": "^16.4.7","express": "^4.21.2","express-rate-limit": "^7.5.0",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.24.0",-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.2.2",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0"++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0"},"engines": {"node": ">=22.0.0"@@@ -128,7 +128,8 @@ function createApp(opts = {}) {// ── Machine endpoints ───────────────────────────────────app.get('/api/health', (_req, res) => res.json({ status: 'ok', service: 'openvibe-host', version: VERSION }));-- app.get('/release.json', release.handler);++ // GET /release.json (ADR-016) and POST /release-metrics: open tabs' update reports into /metrics.++ release.mount(app, { registry: registry });const readiness = createHostReadiness({ store, blobs, auth, outbox, release: release.release, minFreeBytes: () => config.uploads.minFreeBytes });app.get('/api/ready', readiness.handler);app.get('/metrics', sharedMetrics.metricsHandler(registry));@@@ -36,7 +36,10 @@ function createViewerResolver({ auth, config }) {async function fromServiceToken(req, token) {const publicKey = await auth.ensureKey();if (!publicKey) throw new ViewerError(503, 'identity.unavailable', 'the Network signing key is not loaded yet');-- const r = serviceAuth.verifyServiceToken(token, { publicKey, issuer: config.networkUrl, audience: AUDIENCE });++ // acceptSandbox: Host takes sandbox tokens itself and confines them to sandbox projects++ // (projects.js/access.js answer 403 environment.sandbox_token elsewhere); openvibe-contracts++ // >= 0.26.0 refuses them before that unless the receiver opts in.++ const r = serviceAuth.verifyServiceToken(token, { publicKey, issuer: config.networkUrl, audience: AUDIENCE, acceptSandbox: true });if (!r.ok) throw new ViewerError(401, r.code, r.reason);const sub = r.claims.sub;if (sub.startsWith('mod:')) throw new ViewerError(403, 'principal.not_allowed', 'mods cannot manage hosted sites');@@@ -145,6 +145,8 @@ const ORIGIN = 'https://openvibe.host';assert.match(ready.text, /relay off/);const rel = await t.api('GET', '/release.json');assert.strictEqual(rel.json().service, 'host');++ assert.deepStrictEqual(require('openvibe-contracts').validate('registry.release-manifest@1', rel.json()).errors, []);++ assert.strictEqual(rel.json().metrics_url, '/release-metrics');const m = await t.api('GET', '/metrics');assert.strictEqual(m.status, 200);assert.match(m.text, /http_requests_total\{method="GET",route="tenant_site"/);@@@ -63,8 +63,9 @@ async function startNetwork() {function serviceToken(client, cap, extra = {}) {return sign({ sub: `svc:${client}`, aud: ['openvibe.host'], cap, ...extra });}++ // An app token carries its developer project and env, as Network's do (identity.service-token-claims 1.2.0).function appToken(appId, cap, extra = {}) {-- return sign({ sub: `app:${appId}`, actor_type: 'app', aud: ['openvibe.host'], cap, ...extra });++ return sign({ sub: `app:${appId}`, actor_type: 'app', aud: ['openvibe.host'], cap, project_id: 'prj_01J8ZQ4Y7N3M2K1H0G9F8E7D6C', env: 'production', ...extra });}return { ...srv, publicPem, addUser, userToken, fedcmAssertion, serviceToken, appToken, sign };}
@@ -46,10 +46,10 @@ Stage A adds a few safety rules of its own:
@@ -46,10 +46,10 @@ Stage A adds a few safety rules of its own:
## Depends on
## Depends on
-- OpenVibe.Contracts (`openvibe-contracts` v0.24.0; `host.*` capabilities and the `host` manifest are released there and unchanged in v0.30.1): service manifests (vhost rendering, snapshots, the first-party domain list), ids, problem+json, service-token verification, capability checks.
+- OpenVibe.Contracts (`openvibe-contracts` v0.32.0; `host.*` capabilities and the `host` manifest were released in v0.24.0, and v0.32.0 adds the takedown routes to `host.site.manage`): service manifests (vhost rendering, snapshots, the first-party domain list), ids, problem+json, service-token verification, capability checks.
- OpenVibe.Network (Stage B): SSO for the dashboard, the JWKS that verifies user and service tokens, client-credentials tokens for the outbox relay.
- OpenVibe.Network (Stage B): SSO for the dashboard, the JWKS that verifies user and service tokens, client-credentials tokens for the outbox relay.
-- OpenVibe.Events (Stage B): `host.*` events through the `openvibe-sdk` v0.2.2 transactional outbox.
-- `openvibe-shared` v1.3.0 (Stage B): shared chrome, legal pages, `/release.json`, `/metrics`, `/api/ready`.
+- OpenVibe.Events (Stage B): `host.*` events through the `openvibe-sdk` v0.5.0 transactional outbox.
+- `openvibe-shared` v1.5.0 (Stage B): shared chrome, legal pages, `/release.json`, `/metrics`, `/api/ready`.
- OpenVibe.Media: not yet. The roadmap stores artifacts "through Media where practical"; Stage B keeps them on local disk for now (see [Not done yet](#not-done-yet-stage-b)).
- OpenVibe.Media: not yet. The roadmap stores artifacts "through Media where practical"; Stage B keeps them on local disk for now (see [Not done yet](#not-done-yet-stage-b)).
## Stages
## Stages
@@ -245,7 +245,7 @@ People present their Network user JWT as a Bearer token. Services and apps prese
@@ -245,7 +245,7 @@ People present their Network user JWT as a Bearer token. Services and apps prese
Errors are RFC 9457 problems (`application/problem+json`, with the legacy `error` field). A refused upload answers 413/422 with `deploy_id` and the log lines; `503 upload.busy` (with `Retry-After`) when `HOST_MAX_CONCURRENT_UPLOADS` uploads are already being validated; `507 storage.host_full` while the disk has less than `HOST_MIN_FREE_BYTES` free. Network staff (`role: admin`) can read every project, site, deploy and log, delete sites, projects and domains, set quotas, and **take a site or project down** (451 on every host, content kept for review, members see the reason and cannot publish or delete around it); they cannot publish into a tenant's site.
Errors are RFC 9457 problems (`application/problem+json`, with the legacy `error` field). A refused upload answers 413/422 with `deploy_id` and the log lines; `503 upload.busy` (with `Retry-After`) when `HOST_MAX_CONCURRENT_UPLOADS` uploads are already being validated; `507 storage.host_full` while the disk has less than `HOST_MIN_FREE_BYTES` free. Network staff (`role: admin`) can read every project, site, deploy and log, delete sites, projects and domains, set quotas, and **take a site or project down** (451 on every host, content kept for review, members see the reason and cannot publish or delete around it); they cannot publish into a tenant's site.
-The three capability ids and the service manifest are released in `openvibe-contracts` (since v0.24.0, identical in v0.30.1), and the CI contract check is blocking. [`docs/capabilities-proposal/`](docs/capabilities-proposal/) mirrors them; `host.site.manage` there also lists the four takedown routes, which the next Contracts release should add (see [docs/launch.md](docs/launch.md#the-window-in-this-order)).
+The three capability ids and the service manifest are released in `openvibe-contracts` (since v0.24.0; v0.32.0 adds the takedown routes to `host.site.manage`), and the CI contract check is blocking. [`docs/capabilities-proposal/`](docs/capabilities-proposal/) mirrors them; `host.site.manage` there also lists the four takedown routes, which the next Contracts release should add (see [docs/launch.md](docs/launch.md#the-window-in-this-order)).
### Activation and rollback
### Activation and rollback
@@ -253,7 +253,7 @@ Activation is one SQLite transaction: a compare-and-set on the site's `active_de
@@ -253,7 +253,7 @@ Activation is one SQLite transaction: a compare-and-set on the site's `active_de
### Events
### Events
-Through the `openvibe-sdk` v0.2.2 transactional outbox (`event_outbox`), inside the transaction that makes the change:
+Through the `openvibe-sdk` v0.5.0 transactional outbox (`event_outbox`), inside the transaction that makes the change:
| Event | When | Payload |
| Event | When | Payload |
|---|---|---|
|---|---|---|
@@ -266,7 +266,7 @@ The relay publishes with Host's service token (`events.event.publish`, audience
@@ -266,7 +266,7 @@ The relay publishes with Host's service token (`events.event.publish`, audience
### Dashboard
### Dashboard
-Server-rendered pages with the shared chrome (`openvibe-shared` v1.3.0: navbar and theme loader from the Network, `<noscript>` navigation, SSR footer, app icon, legal pages). Every action is a plain form, so it works without JavaScript: projects, quotas and usage, members, sites, folder or archive upload, deploys with activate/rollback/delete, activation history, upload logs and file lists, domains with their DNS records and a "check DNS now" button. Pages are `private, no-store`, `noindex`, `frame-ancestors 'none'`.
+Server-rendered pages with the shared chrome (`openvibe-shared` v1.5.0: navbar and theme loader from the Network, `<noscript>` navigation, SSR footer, app icon, legal pages). Every action is a plain form, so it works without JavaScript: projects, quotas and usage, members, sites, folder or archive upload, deploys with activate/rollback/delete, activation history, upload logs and file lists, domains with their DNS records and a "check DNS now" button. Pages are `private, no-store`, `noindex`, `frame-ancestors 'none'`.
### Observability
### Observability
@@ -277,7 +277,7 @@ Server-rendered pages with the shared chrome (`openvibe-shared` v1.3.0: navbar a
@@ -277,7 +277,7 @@ Server-rendered pages with the shared chrome (`openvibe-shared` v1.3.0: navbar a
- OpenVibe.Network OAuth client **`host`**, redirect `https://openvibe.host/auth/callback`, scope `profile theme`. The same client is the service principal `svc:host`.
- OpenVibe.Network OAuth client **`host`**, redirect `https://openvibe.host/auth/callback`, scope `profile theme`. The same client is the service principal `svc:host`.
- Grant `[host, events.event.publish, openvibe.events]`.
- Grant `[host, events.event.publish, openvibe.events]`.
- Callers of Host get `[<client>, host.site.manage | host.deploy.create | host.domain.manage, openvibe.host]` as needed. None exist yet (Codes, the expected first caller, is not built).
- Callers of Host get `[<client>, host.site.manage | host.deploy.create | host.domain.manage, openvibe.host]` as needed. None exist yet (Codes, the expected first caller, is not built).
-- Released in `openvibe-contracts` v0.24.0 (CI contract check blocking). Next release: add the takedown routes to `host.site.manage.implementedBy` (docs/launch.md).
+- Released in `openvibe-contracts` v0.24.0 (CI contract check blocking); v0.32.0 adds the takedown routes to `host.site.manage.implementedBy`.
### Deploying Stage B (for the operator)
### Deploying Stage B (for the operator)
@@ -9,9 +9,9 @@
@@ -9,9 +9,9 @@
"domain": "openvibe.host",
"domain": "openvibe.host",
"tenantDomain": "*.openvibe.host",
"tenantDomain": "*.openvibe.host",
"packages": [
"packages": [
- "openvibe-contracts v0.24.0 (host.* capabilities and manifest released; unchanged in v0.30.1)",
- "openvibe-sdk v0.2.2",
- "openvibe-shared v1.3.0"
+ "openvibe-contracts v0.32.0 (host.* capabilities and manifest released in v0.24.0; v0.32.0 adds the takedown routes to host.site.manage)",
+ "openvibe-sdk v0.5.0",
+ "openvibe-shared v1.5.0"
],
],
"capabilities": [
"capabilities": [
"host.site.manage",
"host.site.manage",
@@ -38,6 +38,6 @@
@@ -38,6 +38,6 @@
"placeholderInSites": true,
"placeholderInSites": true,
"adoptedByServices": [],
"adoptedByServices": [],
"updated": "2026-09-23",
"updated": "2026-09-23",
- "capabilitiesNote": "released in openvibe-contracts v0.24.0 and identical in v0.30.1; the next release should add the four takedown routes to host.site.manage.implementedBy (docs/launch.md)",
+ "capabilitiesNote": "released in openvibe-contracts v0.24.0; v0.32.0 adds the four takedown routes to host.site.manage.implementedBy",
"launchReady": true
"launchReady": true
}
}
@@ -146,9 +146,8 @@ restart `openvibe-network`, poll `/api/ready`).
@@ -146,9 +146,8 @@ restart `openvibe-network`, poll `/api/ready`).
**6. Host's own records.** In a follow-up Host commit, set README **Status**, `STATUS.json`
**6. Host's own records.** In a follow-up Host commit, set README **Status**, `STATUS.json`
(`"deployed"`, `"placeholderInSites": false`) and the Launch rule note to *launched on <date>*.
(`"deployed"`, `"placeholderInSites": false`) and the Launch rule note to *launched on <date>*.
-**Contracts** (no runtime effect, whenever the next release is cut): in
-`manifests/capabilities/host.site.manage.json`, add the takedown routes and sentence from this
-repository's `docs/capabilities-proposal/host.site.manage.json`:
+**Contracts** (done in openvibe-contracts v0.32.0, which Host pins): `host.site.manage` lists the
+takedown routes from this repository's `docs/capabilities-proposal/host.site.manage.json`:
- `POST /api/v1/projects/:id/takedown`
- `POST /api/v1/projects/:id/takedown`
- `DELETE /api/v1/projects/:id/takedown`
- `DELETE /api/v1/projects/:id/takedown`
@@ -16,9 +16,9 @@
@@ -16,9 +16,9 @@
"dotenv": "^16.4.7",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"express": "^4.21.2",
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.24.0",
- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.2.2",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0"
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0"
},
},
"bin": {
"bin": {
"ovhost": "bin/ovhost"
"ovhost": "bin/ovhost"
@@ -1412,9 +1412,9 @@
@@ -1412,9 +1412,9 @@
}
}
},
},
"node_modules/openvibe-contracts": {
"node_modules/openvibe-contracts": {
- "version": "0.24.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.24.0",
- "integrity": "sha512-8cG+TyI7DAEF1x2MAKlc6Y2AiKWArICYj0QNX4DAbTsOT+ENVTFN5AI35oByz3B1AvBX3nHUNFCK7IM1Zy/+QA==",
+ "version": "0.32.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",
+ "integrity": "sha512-ntN58SIG03YDxbhsiIzOM5GRH19jRMHZUYWI3xU2VwovExagF5DdwApLVfezsu84UTJ+V2Jfn+LWDc+Oz8ab+g==",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -1428,18 +1428,18 @@
@@ -1428,18 +1428,18 @@
}
}
},
},
"node_modules/openvibe-sdk": {
"node_modules/openvibe-sdk": {
- "version": "0.2.2",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.2.2",
- "integrity": "sha512-kDifUp3EcmnTWzElAmaCZnX659WErcnm4+qVEoAiYQWX/w/1p4TWvoHbUPsrX65TSROVR3tiyVkl801n/0yyCw==",
+ "version": "0.5.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
+ "integrity": "sha512-QVJkR5iazQX0ZwIrDp2A88ZeYYBSXEmSzhpgURPUSnzezIM3jz0HBhVKowyhkvg2aBO61XNf77eb4zJGkMjZiw==",
"license": "MIT",
"license": "MIT",
"engines": {
"engines": {
"node": ">=20"
"node": ">=20"
}
}
},
},
"node_modules/openvibe-shared": {
"node_modules/openvibe-shared": {
- "version": "1.3.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0",
- "integrity": "sha512-w8yl/5+9eend8I2O/DzAJnEAL2N3GTUY7c7pzK+MLJuLAGD8FmJJQ9gwbgyrq70Snjt3ebrg3TlpORhRAp2kgA==",
+ "version": "1.5.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0",
+ "integrity": "sha512-cehnscuYMkJwuCYGcI6wPSswbUn0bhTyGAaD5feNCoO//2e9gyC7cgjkEcIOtM8DQF3yTuUNyXnOwckIeM4fUg==",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
"jsonwebtoken": "^9.0.0"
"jsonwebtoken": "^9.0.0"
@@ -31,9 +31,9 @@
@@ -31,9 +31,9 @@
"dotenv": "^16.4.7",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"express": "^4.21.2",
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.24.0",
- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.2.2",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.3.0"
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.32.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.0"
},
},
"engines": {
"engines": {
"node": ">=22.0.0"
"node": ">=22.0.0"
@@ -128,7 +128,8 @@ function createApp(opts = {}) {
@@ -128,7 +128,8 @@ function createApp(opts = {}) {
// ── Machine endpoints ───────────────────────────────────
// ── Machine endpoints ───────────────────────────────────
app.get('/api/health', (_req, res) => res.json({ status: 'ok', service: 'openvibe-host', version: VERSION }));
app.get('/api/health', (_req, res) => res.json({ status: 'ok', service: 'openvibe-host', version: VERSION }));
- app.get('/release.json', release.handler);
+ // GET /release.json (ADR-016) and POST /release-metrics: open tabs' update reports into /metrics.
+ release.mount(app, { registry: registry });
const readiness = createHostReadiness({ store, blobs, auth, outbox, release: release.release, minFreeBytes: () => config.uploads.minFreeBytes });
const readiness = createHostReadiness({ store, blobs, auth, outbox, release: release.release, minFreeBytes: () => config.uploads.minFreeBytes });
app.get('/api/ready', readiness.handler);
app.get('/api/ready', readiness.handler);
app.get('/metrics', sharedMetrics.metricsHandler(registry));
app.get('/metrics', sharedMetrics.metricsHandler(registry));
@@ -36,7 +36,10 @@ function createViewerResolver({ auth, config }) {
@@ -36,7 +36,10 @@ function createViewerResolver({ auth, config }) {
async function fromServiceToken(req, token) {
async function fromServiceToken(req, token) {
const publicKey = await auth.ensureKey();
const publicKey = await auth.ensureKey();
if (!publicKey) throw new ViewerError(503, 'identity.unavailable', 'the Network signing key is not loaded yet');
if (!publicKey) throw new ViewerError(503, 'identity.unavailable', 'the Network signing key is not loaded yet');
- const r = serviceAuth.verifyServiceToken(token, { publicKey, issuer: config.networkUrl, audience: AUDIENCE });
+ // acceptSandbox: Host takes sandbox tokens itself and confines them to sandbox projects
+ // (projects.js/access.js answer 403 environment.sandbox_token elsewhere); openvibe-contracts
+ // >= 0.26.0 refuses them before that unless the receiver opts in.
+ const r = serviceAuth.verifyServiceToken(token, { publicKey, issuer: config.networkUrl, audience: AUDIENCE, acceptSandbox: true });
if (!r.ok) throw new ViewerError(401, r.code, r.reason);
if (!r.ok) throw new ViewerError(401, r.code, r.reason);
const sub = r.claims.sub;
const sub = r.claims.sub;
if (sub.startsWith('mod:')) throw new ViewerError(403, 'principal.not_allowed', 'mods cannot manage hosted sites');
if (sub.startsWith('mod:')) throw new ViewerError(403, 'principal.not_allowed', 'mods cannot manage hosted sites');
@@ -145,6 +145,8 @@ const ORIGIN = 'https://openvibe.host';
@@ -145,6 +145,8 @@ const ORIGIN = 'https://openvibe.host';
assert.match(ready.text, /relay off/);
assert.match(ready.text, /relay off/);
const rel = await t.api('GET', '/release.json');
const rel = await t.api('GET', '/release.json');
assert.strictEqual(rel.json().service, 'host');
assert.strictEqual(rel.json().service, 'host');
+ assert.deepStrictEqual(require('openvibe-contracts').validate('registry.release-manifest@1', rel.json()).errors, []);
+ assert.strictEqual(rel.json().metrics_url, '/release-metrics');
const m = await t.api('GET', '/metrics');
const m = await t.api('GET', '/metrics');
assert.strictEqual(m.status, 200);
assert.strictEqual(m.status, 200);
assert.match(m.text, /http_requests_total\{method="GET",route="tenant_site"/);
assert.match(m.text, /http_requests_total\{method="GET",route="tenant_site"/);
@@ -63,8 +63,9 @@ async function startNetwork() {
@@ -63,8 +63,9 @@ async function startNetwork() {
function serviceToken(client, cap, extra = {}) {
function serviceToken(client, cap, extra = {}) {
return sign({ sub: `svc:${client}`, aud: ['openvibe.host'], cap, ...extra });
return sign({ sub: `svc:${client}`, aud: ['openvibe.host'], cap, ...extra });
}
}
+ // An app token carries its developer project and env, as Network's do (identity.service-token-claims 1.2.0).
function appToken(appId, cap, extra = {}) {
function appToken(appId, cap, extra = {}) {
- return sign({ sub: `app:${appId}`, actor_type: 'app', aud: ['openvibe.host'], cap, ...extra });
+ return sign({ sub: `app:${appId}`, actor_type: 'app', aud: ['openvibe.host'], cap, project_id: 'prj_01J8ZQ4Y7N3M2K1H0G9F8E7D6C', env: 'production', ...extra });
}
}
return { ...srv, publicPem, addUser, userToken, fedcmAssertion, serviceToken, appToken, sign };
return { ...srv, publicPem, addUser, userToken, fedcmAssertion, serviceToken, appToken, sign };
}
}