openvibers/openvibe.media Commits
agent: a Media tenant for the screenshot pass (plan L9) (#318)
Changes
@@@ -2,6 +2,10 @@## Unreleased++- A tenant for the screenshot pass (plan L9, devblog §3.3): OpenVibe.Host stores the PNGs it captures (ovhost shots) in its own++ tenant, `host`, through the Object API v2. The tenant is created at boot like the other first-party ones, and reached only++ with a Network service token that holds the media.object.* verbs for the host namespace.++- A deleted recording's or clip's thumbnail goes with it (2026-10-20). Migration 0013 adds a trigger that marks anobject's derivatives (`media_variants`) deleted whenever it is, and backfills the ones left behind; restoring a nativeobject brings back the derivatives deleted with it. `/t/vod-<id>-…` and `/t/clip-<id>-…` answer the empty pixel once@@@ -212,6 +212,7 @@ async function ensureTokenOnlyApps() {['inventory', 'OpenVibe.Inventory', 1, []], // Workshop badge images['media-hub', 'OpenVibe.MediaHub', 100, []], // the OpenVibe.Download drive['codes', 'OpenVibe.Codes', 5, []], // a task's screenshots, private to the person (Codes #188, 10-30)++ ['host', 'OpenVibe.Host', 10, []], // the screenshot baselines ovhost shots captures (plan L9, 11-06)]) {if (!await db.getApp(appId)) {await db.run("INSERT INTO apps (app_id, name, api_key_hash, quota_bytes, allowed_origins) VALUES (?, ?, '', ?, ?)", [appId, name, quotaGb * 1024 ** 3, JSON.stringify(origins)]);@@@ -36,7 +36,7 @@ const crypto = require('crypto');await auth.ensureTokenOnlyApps();// C-71: every first-party tenant exists without seeding, and one boot created carries no app key. An existing row// (live, written with a test key just above) is left exactly as it was.-- for (const id of ['community', 'tools', 'games', 'wiki', 'blog', 'inventory', 'media-hub', 'codes']) {++ for (const id of ['community', 'tools', 'games', 'wiki', 'blog', 'inventory', 'media-hub', 'codes', 'host']) {const row = await db.getApp(id);assert.ok(row, `${id} exists without seeding`);assert.strictEqual(row.api_key_hash, '', `${id} has no app key`);
@@ -2,6 +2,10 @@
@@ -2,6 +2,10 @@
## Unreleased
## Unreleased
+- A tenant for the screenshot pass (plan L9, devblog §3.3): OpenVibe.Host stores the PNGs it captures (ovhost shots) in its own
+ tenant, `host`, through the Object API v2. The tenant is created at boot like the other first-party ones, and reached only
+ with a Network service token that holds the media.object.* verbs for the host namespace.
+
- A deleted recording's or clip's thumbnail goes with it (2026-10-20). Migration 0013 adds a trigger that marks an
- A deleted recording's or clip's thumbnail goes with it (2026-10-20). Migration 0013 adds a trigger that marks an
object's derivatives (`media_variants`) deleted whenever it is, and backfills the ones left behind; restoring a native
object's derivatives (`media_variants`) deleted whenever it is, and backfills the ones left behind; restoring a native
object brings back the derivatives deleted with it. `/t/vod-<id>-…` and `/t/clip-<id>-…` answer the empty pixel once
object brings back the derivatives deleted with it. `/t/vod-<id>-…` and `/t/clip-<id>-…` answer the empty pixel once
@@ -212,6 +212,7 @@ async function ensureTokenOnlyApps() {
@@ -212,6 +212,7 @@ async function ensureTokenOnlyApps() {
['inventory', 'OpenVibe.Inventory', 1, []], // Workshop badge images
['inventory', 'OpenVibe.Inventory', 1, []], // Workshop badge images
['media-hub', 'OpenVibe.MediaHub', 100, []], // the OpenVibe.Download drive
['media-hub', 'OpenVibe.MediaHub', 100, []], // the OpenVibe.Download drive
['codes', 'OpenVibe.Codes', 5, []], // a task's screenshots, private to the person (Codes #188, 10-30)
['codes', 'OpenVibe.Codes', 5, []], // a task's screenshots, private to the person (Codes #188, 10-30)
+ ['host', 'OpenVibe.Host', 10, []], // the screenshot baselines ovhost shots captures (plan L9, 11-06)
]) {
]) {
if (!await db.getApp(appId)) {
if (!await db.getApp(appId)) {
await db.run("INSERT INTO apps (app_id, name, api_key_hash, quota_bytes, allowed_origins) VALUES (?, ?, '', ?, ?)", [appId, name, quotaGb * 1024 ** 3, JSON.stringify(origins)]);
await db.run("INSERT INTO apps (app_id, name, api_key_hash, quota_bytes, allowed_origins) VALUES (?, ?, '', ?, ?)", [appId, name, quotaGb * 1024 ** 3, JSON.stringify(origins)]);
@@ -36,7 +36,7 @@ const crypto = require('crypto');
@@ -36,7 +36,7 @@ const crypto = require('crypto');
await auth.ensureTokenOnlyApps();
await auth.ensureTokenOnlyApps();
// C-71: every first-party tenant exists without seeding, and one boot created carries no app key. An existing row
// C-71: every first-party tenant exists without seeding, and one boot created carries no app key. An existing row
// (live, written with a test key just above) is left exactly as it was.
// (live, written with a test key just above) is left exactly as it was.
- for (const id of ['community', 'tools', 'games', 'wiki', 'blog', 'inventory', 'media-hub', 'codes']) {
+ for (const id of ['community', 'tools', 'games', 'wiki', 'blog', 'inventory', 'media-hub', 'codes', 'host']) {
const row = await db.getApp(id);
const row = await db.getApp(id);
assert.ok(row, `${id} exists without seeding`);
assert.ok(row, `${id} exists without seeding`);
assert.strictEqual(row.api_key_hash, '', `${id} has no app key`);
assert.strictEqual(row.api_key_hash, '', `${id} has no app key`);