Commits
-
Emit Media storage and delivery usage readings to Billing through openvibe-sdk/usage (off by default); pin openvibe-sdk v0.32.0 (#43)OpenVibers · 5 Oct 2026
3e029a1 -
Generate a 720p rendition on demand and list it in the master playlist (F4 slice 1, off by default) (#42)OpenVibers · 5 Oct 2026
166fe22 -
Compose Media's pages through openvibe-shared/shell; add /llms-full.txt and IndexNow; pin openvibe-shared v2.10.0 (#41)OpenVibers · 5 Oct 2026
8d41bca -
Expose the timeline HLS URL on the v1 VOD and clip API (#36)OpenVibers · 5 Oct 2026
79295af -
Move Media's shutdown onto openvibe-sdk/service gracefulStop with the 70 s drain (#33)OpenVibers · 5 Oct 2026
809ddfc -
Delete the read-only pastes app API and its handlers (#37)OpenVibers · 5 Oct 2026
77124cf -
Route every storage class through the placement sweep (#32)OpenVibers · 5 Oct 2026
6a1195d -
Queue an object.pack after every finished CMAF cut (#31)OpenVibers · 5 Oct 2026
d5deebb -
Serve clips as virtual windows over the source's timeline (#30)OpenVibers · 5 Oct 2026
2a55cab -
Queue the CMAF timeline at finalize and sign playlists for longerOpenVibers · 4 Oct 2026
76f8590 -
Document the object backfill as a boot-time module and the drift reportOpenVibers · 4 Oct 2026
29cf553 -
Pack durable CMAF segments into ~60 s chunks and serve them as rangesOpenVibers · 4 Oct 2026
3573f61 -
Index video objects as CMAF segments and serve HLS from the timelineOpenVibers · 4 Oct 2026
214a810 -
Bump openvibe-shared v2.3.1 → v2.5.0 (#16)OpenVibers · 3 Oct 2026
6fc4e52 -
Align version pins and declare shield, lineage and paste-route env vars (#13)OpenVibers · 2 Oct 2026
fa04eb8 -
Plan T4: steps 7 and 8 of the ordered cleanup brief in… (#10)OpenVibers · 2 Oct 2026
aab4b07 -
Make the paste API read-only and answer 410 pastes.moved to writes (#9)OpenVibers · 2 Oct 2026
7f81034 -
Add per-app webhook send tallies and an operator webhooks sectionOpenVibers · 2 Oct 2026
3c2d617 -
X-Internal-Key is gone from Media (plan T2, the last step): POST /internal/avatar-ingest takes only a Network service token holding media.avatar.ingest, loopback only; guardOrKey, config.network.internalApiKey and INTERNAL_API_KEY are deleted (production no longer sets it), and the visitor-hash secret is VIEW_HASH_SECRET or MEDIA_SECRET with a named warning when neither is set in production. Tests: token with the capability passes, without it 403, wrong audience and sandbox 401, no credential or a forged Bearer refused. npm test and test:pg 75/76, 1 skipped (verified).OpenVibers · 29 Sept 2026
809b06b -
The SQLite era is gone (on PostgreSQL since 2026-09-28; no compatibility kept): the one-time migrate-to-postgres script, the *_DB_PATH settings (config, .env.example, systemd unit, test helpers), the unused better-sqlite3 dependency and its lockfile entries, and docs, docstrings and STATUS lines about SQLite or its rollback window. observability.test.js waits up to 30 s for the real server to boot (a cold PGlite boot plus migrations missed the 10 s window on a loaded machine). Reviewed independently (VERDICT: SHIP). npm test 64/65 test files passed, 1 skipped (n-1.test.js: N-1 SQL: skipped (the recorded N-1 releas.OpenVibers · 29 Sept 2026
dbdcbee -
Media on PostgreSQL (ADR-035, roadmap WS-X2): openvibe-sdk/db, async throughout, jobs and quotas safe under concurrency.OpenVibers · 28 Sept 2026
e3f2239 -
npm test no longer counts a skipped test as passed (roadmap WS-Q task 7; Shared v1.25.0 openvibe-shared/test-runner). The test script was a shell loop that stopped at the first failure and counted nothing, so a test that printed "⚠️ ffmpeg not found: … skipped" and exited 0 was as good as a pass. test/run.js is now the shared runner: every file, one at a time as before (parallel 1), a 300 s timeout per file, and a summary; a file that prints "<label>: skipped (<why>)" is listed with ○ and not counted as passed, and --strict fails on it. The six tests that skipped in free-form words now print that line: jobs-invariant (split/remux execution), clip-cut-job (clip.cut), jobs-previews (preview jobs), duration-reconcile (measurement checks) and vod-duration (real-file finalize check) without ffmpeg, and trust-proxy (direct-caller check) without a non-loopback interface. openvibe-shared is pinned at v1.25.0 (from v1.24.0), named in the README. npm test (Node 22) 64/64 test files passed.OpenVibers · 28 Sept 2026
47b21bd -
Media's readiness has no fake green (roadmap WS-Q task 7, Shared v1.24.0). events_outbox answered ok with { enabled: false } while the relay was off, and an unconfigured remote tier (b2, r2) had no check at all, so nothing showed it was not in use. The relay off is now skipped with the reason (detail { enabled: false } kept), and an unconfigured tier is listed as remote_<name> skipped "not configured"; neither is ever ok, and an optional skip leaves a service whose checks passed ready. Drill mode still lists no remote tier. openvibe-shared is pinned at v1.24.0 (from v1.22.0), named in the README. test/observability.test.js checks both skips (the configured-tier and failure cases are unchanged). npm test passes.OpenVibers · 28 Sept 2026
51283d3 -
README and STATUS.json are current (roadmap WS-U task 3): the README gains Purpose (the opening overview), Owns, Does not own, Depends on, Capabilities (the nine media.* capabilities and the grants Media uses at Network, Live and Events), Acceptance (what the test files prove), Security (SECURITY.md, auth, private data, exposure, egress, secrets by name) and Deploy (sudo ovhost deploy media, the recording guard, unit, port, env file, vhost, N-1, rollback and its media_jobs blocker) sections. Stale facts corrected: pastes belong to OpenVibe.Community since 2026-09-22 (the overview and the /p/:slug row said Media is their canonical home), and the layout names the current pins (shared v1.22.0, contracts v0.71.0, sdk v0.12.0). STATUS.json is new, in the shape of Search's, updated 2026-09-28.OpenVibers · 28 Sept 2026
14cac06 -
Per-actor rate limits at Media's capability boundaries (roadmap WS-R task 4, openvibe-sdk v0.12.0 limits). server/actor-limits.js makes one limiter that counts each caller: the Network token's principal (svc:community, app:app_… of a developer project), the person signed in to /api/v2/me, or an app acting for one of its users (X-OV-User-Id, counted per user). An app's own API key is not counted, since that first-party server speaks for every visitor of its site and limits them itself; a browser with only an upload token is counted by address. Past a limit the route answers 429 problem+json rate_limited with Retry-After before any work, logs one [Limits] line and counts media_rate_limited_total{limit,window}.OpenVibers · 28 Sept 2026
1125a3c -
A call acting for one user no longer writes another user's media (roadmap WS-R task 5; medium): with the app key and X-OV-User-Id, which is meant to carry that user's permissions, user B could update (visibility included), delete, finalize, feed chunks to, start or stop ingest on and re-cut user A's VODs and clips, replace their thumbnails, place and release retention holds on v2 objects (documented app key only), and reach every admin storage route but holds and ops (bulk delete, tier moves, settings, and GET /holds with staff notes). v1 VOD and clip writes and the thumbnail route now take only the acting user's own rows: someone else's private one answers exactly as a missing id, anything else 403 (the chunk and finalize check used to answer 403 for a private one too); v2 hold writes refuse acting calls with 403 media.hold.forbidden; admin storage refuses them on every route (403 media.admin.forbidden; holds and ops keep their codes). The app key alone is unchanged, and Live does not send X-OV-User-Id on any of these. test/security-idor.test.js swaps ids between two users of an app, two apps (through each tenant), two developer projects and two namespaces of one tenant on every v2 object route (read, download, delete, restore, holds, upload URL, content, complete, multipart start/status/part/complete/abort, lists), upload and multipart tokens of another object or session, jobs (read, create, approve, cancel), namespaces, and v1 VODs, clips, thumbnails, files, assets and admin storage, and checks nothing changed. Checked by breaking it: each of the five previous route files fails it.OpenVibers · 26 Sept 2026
62160a8 -
Tiering for native v2 objects (roadmap WS-G task 11): popular objects get a verified R2 copy that /o/:id plays from, behind a site-level activation gate that is off by default. Popularity (server/objects/popularity.js, ADR-021): GET /o/:id counts a native object's viewer once per UTC day as HMAC-SHA256(the day's random salt, client network; IPv6 by /64), never an IP address or subject id; the day's hashes and salt are deleted once it is over (first view of the next day, hourly, every sweep), only daily counts stay (30 days); the 7-day figure sums them. Seeks, HEAD, Sec-GPC/DNT, crawlers and sandbox tenants are not counted. Policy: the revisioned namespace media.object_tier (server/objects/tier-policy.js; active=false, promoteMinUniqueViewers7d 500, viewed within 3 days, 16 MB to 4 GB, 3 promotions and 10 demotions per sweep, demote after 14 idle days), validated (types, ranges, min size below max, demoteIdleDays above promoteRecentAccessDays) and served by the admin config routes. Sweep and moves (server/objects/tiering.js), step 4 of the storage sweep, skipped under disk pressure and in a restore drill: with the gate off nothing moves (sweep or direct call) and would-be moves are logged as dry_run; holds refuse every move; only an object whose canonical copy is verified on record is promoted, the canonical copy is re-hashed, copied to R2 and the R2 copy's size and sha256 read back before it is recorded (else deleted, failed, 6 h back-off); a demotion (idle, or no longer ready) re-checks the canonical copy first and never removes the last good copy, and the purge waits for a deleted object's R2 copy. Every decision goes to media_object_tier_decisions with its inputs and the policy in force; GET /admin/storage/tiers/objects/policy and /decisions, /me/ops (native_objects) and media_tier_decisions_24h{target=vod|object} show them. The storage engine now exports uploadFile, which the scheduled verification's default re-upload called but could not reach. test/object-tiering.test.js runs it against a fake S3 through the real SDK.OpenVibers · 26 Sept 2026
43990ec -
N-1 tests (roadmap WS-P task 11): the previous release's clients against this server, and its SQL against this schema. test/n-1.test.js boots this checkout as a real server/index.js in the drill sandbox (MEDIA_DRILL: loopback, no outbound connection, program or background job; test/n-1/preload.js lets writes through) on a database created from the previous release's schema, so Media's migrations run over it, and replays test/fixtures/n-1/client.json inside openvibe-shared's mixed-version matrix (release-compat assertMixedVersion): 64 requests (the openvibe-sdk media and objects clients, anonymously and with an app key as the SDK signs in: files v1, Object API v2 and jobs; the object explorer; and every player URL, link, script and form of the /, /browse, /v, /c, /p, /me and /updates pages that release served), each answered compatibly (any 2xx for a 2xx, the same status otherwise, JSON where there was JSON, redirects to the same place, every response field the client reads with a type it had; a route that answers as an unknown path fails). Then every statement in test/fixtures/n-1/worker.json (331: what that release ran, plus its SQL literals, workers included) must still prepare on the migrated schema, and no old INSERT may miss a new NOT NULL column without a default. Fixtures recorded from b7fbe65, the release in production; refresh after each deploy with `npm run n-1:record [ref]` (a temporary worktree of the release, seeded, booted, replayed). The harness is the file Live, Chat and Community share (test/n-1/harness.js); test/n-1/service.js is Media's part. In npm test, so in CI, no git history or network. Checked by breaking it: renaming /v/:id/transcript.json and /p/:slug/raw fails it.OpenVibers · 26 Sept 2026
44bc3f2 -
Object explorer (roadmap WS-G task 12): openvibe.media/me shows a signed-in person the objects whose owner_subject is their Network subject, in every tenant, and /me/ops gives Network staff the operator viewsOpenVibers · 26 Sept 2026
15b8f93 -
Namespaces and grants (roadmap WS-G task 2): every namespace is a media_namespaces row (tenant, owner, policy, quota_bytes/quota_objects, used/reserved snapshot). A tenant's root is its app id, or app.<project_id> / app.<project_id>.sandbox for a developer project; children (<root>.<segment>, up to 3, MEDIA_NAMESPACE_MAX_CHILDREN per tenant) are made by the first init that names one. Network tokens are checked per namespace for five verbs (server/auth.js VERBS): read, list (media.object.list), write, delete (media.object.delete), transform (media.derivative.create); the older ids keep granting the newer verbs (read lists, upload deletes and transforms) unless the namespace's policy says strict_verbs, and an app token's legacy ns [prj_…] means app.<project_id>.*. Quotas cover a namespace's subtree up to the tenant (bytes: the root inherits apps.quota_bytes): init reserves the declared size in the same transaction as the object (413 media.quota.exceeded / media.quota.objects_exceeded), the stored bytes re-reserve, complete re-checks the real size and settles, a multipart abort or expiry releases, and the hourly sweep fails uploads whose reservation passed MEDIA_UPLOAD_RESERVATION_HOURS (72) and frees their bytes. Policy: kinds, visibilities, max_object_bytes. Owner read GET /api/v2/:app/namespaces[/:namespace]; operator scripts/namespaces.js (list, --set quotas and policy). Backfill on open (idempotent): developer-project objects move to app.<project_id>[.sandbox], every tenant and named namespace gets a row, and once, uploads in progress get a reservation for the full window. Test: namespaces.test.js (new); docs/object-model.md, READMEOpenVibers · 26 Sept 2026
09f3e0d