openvibers/openvibe.media .env.example

.env.example

main 9.9 KB · 178 lines · plaintext

1# ── OpenVibe.Media — environment ─────────────────────────────
2# Copy to .env (or /etc/openvibe/media.env in production) and adjust.
3
4# HTTP
5PORT=4100
6HOST=0.0.0.0
7NODE_ENV=development
8# Public base URL used when building absolute playback/asset URLs.
9MEDIA_PUBLIC_URL=https://openvibe.media
10
11# PostgreSQL (ADR-035), written by OpenVibe.Host roles/data/add-service.sh media: the runtime role through PgBouncer,
12# the owner on a direct connection (migrations), and Valkey (shared limit counters).
13# Without DATABASE_URL, development uses an embedded PGlite database in data/pglite (MEDIA_PGLITE_DIR overrides it).
14DATABASE_URL=
15DATABASE_DIRECT_URL=
16VALKEY_URL=
17VALKEY_PREFIX=
18# The service's data directory: the reports (duration reconcile, storage orphans, H15 repair) and the drill
19# artifacts are written under it.
20MEDIA_DATA_DIR=./data
21
22# Restore-drill mode, set only by `ovhost drill media` (OpenVibe.Host docs/restore-drills.md): reads
23# only from a restored copy, no stored bytes, no jobs, events or ffmpeg; refuses to start
24# unless DATABASE_URL names the restored copy (never ov_media on 5432/6432), DATABASE_DIRECT_URL is unset,
25# HOST is loopback and PORT is not 4100 (server/drill.js).
26# MEDIA_DRILL=1
27
28# Storage paths (all created on boot)
29VOD_PATH=./data/vods
30CLIPS_PATH=./data/clips
31PASTES_PATH=./data/pastes
32# Where pastes live now (2026-09-22): /p/:slug and /p/:slug/raw are 301s there, always — the default
33# below applies when unset. Stored screenshot bytes under PASTES_PATH/screenshots are still served,
34# and every write to the paste API answers 404.
35#PASTES_MOVED_TO=https://openvibe.community
36THUMBNAILS_PATH=./data/thumbnails
37FILES_PATH=./data/files
38
39# RTP ingest port pool (UDP, bound on 127.0.0.1 by ffmpeg)
40RTP_PORT_MIN=12000
41RTP_PORT_MAX=12199
42
43# Identity provider (OpenVibe.Network) — JWKS fetched at boot for offline
44# RS256 verification of user JWTs on browser endpoints.
45OV_NETWORK_URL=https://openvibe.network
46OV_NETWORK_INTERNAL_URL=http://127.0.0.1:4000
47# Owner subjects (docs/object-model.md#owner-subjects): objects that name only an app-local owner
48# get the owner's Network subject from POST /internal/identity/resolve-batch, with a service token
49# (OV_OAUTH_CLIENT_ID / OV_OAUTH_CLIENT_SECRET, capability identity.subject.resolve).
50# MEDIA_OWNER_SUBJECT_SYNC=0 stops the reconcile job (before rolling a backfill back).
51MEDIA_OWNER_SUBJECT_SYNC=
52MEDIA_OWNER_SUBJECT_INTERVAL_MIN=10
53
54# OpenVibe.Live lineage (server/lineage-client.js): channel owners are resolved through Live's canonical
55# resolver with Media's service token (OV_OAUTH_CLIENT_ID / OV_OAUTH_CLIENT_SECRET, audience
56# openvibe.live, OV_LIVE_INTERNAL_URL) instead of a local username mapping. MEDIA_LINEAGE=off disables it.
57MEDIA_LINEAGE=
58
59# ── First-party tenants ──────────────────────────────────────
60# Created at boot (server/auth.js ensureTokenOnlyApps) and reached only with each service's Network service token.
61# No app key is issued or seeded (MEDIA_APPS_SEED and MEDIA_APP_KEYS are gone, compatibility register C-71).
62
63# ── Developer-project tenants (ADR-014) ──────────────────────
64# Created on first use by a developer app token (Network, audience openvibe.media) for its own
65# project: /api/v1/prj_<ULID>/files, /api/v2/prj_<ULID>/objects. Default quotas in MB, applied
66# when the tenant is created (change apps.quota_bytes afterwards to adjust one tenant).
67MEDIA_APP_TENANT_QUOTA_MB=1024
68MEDIA_APP_SANDBOX_QUOTA_MB=100
69
70# ── Per-actor rate limits (server/actor-limits.js, roadmap WS-R task 4) ──
71# Requests each caller (a Network token's principal, a signed-in person, an app acting for one of its
72# users) may make to one read route per minute and per hour; past them: 429 rate_limited + Retry-After.
73# Uploads, deletes and jobs have their own, tighter numbers in the routes. An app's own API key is not counted.
74MEDIA_LIMITS_MINUTE=120
75MEDIA_LIMITS_HOUR=3000
76
77# ── Object storage tiering (optional; local-only when unset) ─
78# Backblaze B2 (canonical cold tier)
79MEDIA_B2_ENDPOINT=
80MEDIA_B2_REGION=us-west-004
81MEDIA_B2_BUCKET=
82MEDIA_B2_KEY_ID=
83MEDIA_B2_APP_KEY=
84# Cloudflare R2 (popularity cache, free egress)
85MEDIA_R2_ENDPOINT=
86MEDIA_R2_REGION=auto
87MEDIA_R2_BUCKET=
88MEDIA_R2_ACCESS_KEY_ID=
89MEDIA_R2_SECRET_ACCESS_KEY=
90# Placement capability probe (server/placement/providers.js): each provider is probed for range/206/ETag/If-Range.
91# report (default) records the results (health, admin placement view) and takes nothing out of routing; enforce
92# removes a provider from every class whose requirements its last completed probe failed. Enforce only after the
93# production results were read: S3-compatible APIs differ, and If-Range especially.
94MEDIA_CAPABILITY_GATE=report
95# Placement demand (server/placement/demand.js): the region tag on the Valkey demand counters and the hot sorted
96# set (object × region × 5-minute bucket). One Media host is one region; default local.
97#MEDIA_DEMAND_REGION=local
98# Media shield (server/placement/shield.js): hand eligible public reads to the nginx edge cache
99# (X-Accel-Redirect) instead of streaming them from this process, for the comma-separated providers
100# listed. It answers only on MEDIA_SHIELD_HOST (a DNS-only edge name, never through Cloudflare's proxy);
101# MEDIA_SHIELD_REFRESH_URL is the loopback purge listener used to refresh cached slices.
102#MEDIA_SHIELD=b2
103#MEDIA_SHIELD_HOST=edge.openvibe.media
104#MEDIA_SHIELD_REFRESH_URL=http://127.0.0.1:8479
105
106# ── Recording knobs (defaults match the inherited recorder) ──
107# VOD_STOP_GRACE_MS=60000
108# VOD_DISK_WARN_GB=15
109# VOD_DISK_CRIT_GB=5
110# Under this much free space the largest running recording is stopped (one per minute), so a long stream cannot fill
111# the disk PostgreSQL and every service share. Under VOD_DISK_CRIT_GB only new recordings are refused.
112# VOD_DISK_STOP_GB=3
113# While an MP4 recording is written, its DVR is the playlist over the file itself (/v/:id/dvr.m3u8). 0 also stops the
114# periodic seekable copy of MP4 recordings (twice the disk and the I/O of a long stream) once every DVR player reads
115# the playlist; WebM recordings keep their copy.
116# VOD_DVR_SNAPSHOT_MP4=1
117# MAX_VOD_SIZE_MB=2048
118# VOD_NO_NORMALIZE=0
119
120# OpenVibe.Events (durable events): vod/clip/object/storage outcomes are also published through
121# Media's outbox with the OAuth client credentials (grant events.event.publish). Unset = off.
122#EVENTS_URL=http://127.0.0.1:4300
123
124# IndexNow (openvibe-shared/indexnow): a key makes search engines recrawl a public page when it
125# appears or changes. 8–128 hex/alphanumeric characters; the key file is served at /<key>.txt.
126# Unset = off, no key file, nothing sent.
127#INDEXNOW_KEY=
128
129# ── Object uploads (docs/object-model.md#presigned-uploads, #multipart-uploads) ──
130# MEDIA_SIGNING_SECRET signs presigned upload/download URLs; set it in production.
131#MEDIA_SIGNING_SECRET=
132#MEDIA_OBJECT_MAX_MB=256 # single-part limit; larger objects use multipart
133#MEDIA_UPLOAD_TOKEN_TTL_S=3600 # default presigned upload URL lifetime
134#MEDIA_MULTIPART_MAX_MB=20480
135#MEDIA_MULTIPART_PART_MB=64 # bounds: MEDIA_MULTIPART_MIN_PART_MB=5, MEDIA_MULTIPART_MAX_PART_MB=256
136
137# ── S3 surface and zones (ADR-031; server/s3/, docs/s3.md) ──
138# MEDIA_S3_KEY_SECRET (32+ characters) encrypts the S3 access keys' secrets; unset = the S3 surface answers 503 and no
139# key can be issued. Changing it makes every issued key unusable. MEDIA_S3_HOSTS: the Host names the surface answers.
140#MEDIA_S3_KEY_SECRET=
141#MEDIA_S3_HOSTS=s3.openvibe.media,s3.openvibe.network
142#MEDIA_MULTIPART_TTL_HOURS=24
143#MEDIA_UPLOAD_MIN_FREE_MB=10240 # free disk kept in reserve by multipart uploads and split/remux jobs
144
145# ── Jobs (docs/object-model.md#jobs) ─────────────────────────
146#MEDIA_JOBS_ENABLED=1
147#MEDIA_JOBS_POLL_MS=5000
148#MEDIA_JOBS_LIGHT_CONCURRENCY=2
149#MEDIA_JOBS_HEAVY_CONCURRENCY=1
150#MEDIA_JOBS_HEAVY_WHILE_RECORDING=0
151#MEDIA_INVARIANT_SCAN_HOURS=24 # the size-invariant validator (proposes split/remux; never runs them)
152#MEDIA_MOVE_CLEANUP_MINUTES=60 # storage.move.cleanup: alert on objects whose placement move failed 3 times (0 = never)
153
154# ── Segment-native video (docs/media-fabric.md §3, F3.1) ─────
155# 1 = the object.cmaf job (CMAF segments of a finished video, indexed in media_timeline), the HLS routes
156# /o/:id/master.m3u8, /o/:id/source/… and the hls_url field of GET …/download?format=json ; off (default):
157# the job is refused and the routes 404, nothing else changes.
158#MEDIA_HLS_ENABLED=0
159# Encode incompatible source streams as H.264/AAC (CPU intensive; off by default).
160#MEDIA_CMAF_TRANSCODE=0
161# Maximum duration in seconds of a source needing transcoding (default 4 h).
162#MEDIA_CMAF_TRANSCODE_MAX_SECONDS=14400
163# libx264 threads for one transcode (it also runs at nice 15: the heavy lane only waits for recordings before starting).
164#MEDIA_CMAF_TRANSCODE_THREADS=2
165# Maximum bytes of local timeline segments and packed chunks (default 10 GiB; 0 keeps only durable copies).
166#MEDIA_TIMELINE_LOCAL_MAX_BYTES=10737418240
167# Stop keeping new local timeline copies below this free space (default 8 GiB).
168#MEDIA_TIMELINE_LOCAL_MIN_FREE_BYTES=8589934592
169# At most this many old VODs are lazily queued per hour; recording finalize is exempt (default 20).
170#MEDIA_HLS_BACKFILL_PER_HOUR=20
171# Lifetime in seconds of a signed HLS playlist (download?format=json → hls_url of a private or sandbox object; its own
172# signature purpose, accepted only by the HLS routes). Default 21600 (6 h), clamped to 60..43200.
173#MEDIA_HLS_PLAYLIST_TTL_S=21600
174# Materialized clips (docs/materialized-clips.md, F3.6): 1 = with MEDIA_HLS_ENABLED and a source that has a source
175# timeline, clip.cut materializes the clip over the source's bytes — the interior rows name the source's segments and
176# only the two window edges are re-encoded; 0 (default) = every clip is cut with the full re-encode as before.
177#MEDIA_MATERIALIZED_CLIPS=0
178