Replaces single-MP4 + range-request streaming with HLS multi-bitrate
segments to fix video stutter through the Newt tunnel. Range-request
bursts were the root cause; HLS chunks are small and tunnel-friendly,
plus the player adapts bitrate to bandwidth.
Backend
- New BullMQ `hls-transcode` queue (in-process worker, concurrency 1)
- FFmpeg single-pass transcode → 360p/720p/1080p variants with aligned
keyframes; output at /media/local/hls/{id}/master.m3u8
- New /api/{videos|public}/{id}/hls/* routes serving signed manifests
and segments (URLs emitted as /media/* so nginx rewrites to media-api)
- Prisma: HlsStatus enum + 6 fields on Video + index, migration
- Upload + yt-dlp fetch paths enqueue transcode jobs
- ENABLE_HLS_TRANSCODE flag (default off; gates enqueue only)
- Backfill script: `npm run backfill:hls`
- media-api bumped to 4 CPU / 2G for FFmpeg headroom
Frontend
- New useHls hook: lazy-imports hls.js (kept out of main bundle),
native HLS on Safari/iOS, gives up after 2 NETWORK_ERRORs so MP4
fallback engages cleanly
- VideoPlayer, VideoViewerModal, ShortsPage, ProductDetailPage now
prefer HLS when ready; MP4 fallback is automatic
- ShortsPage prefetches next-3 master manifests via <link rel="prefetch">
- PublicVideoCard hover preview stays MP4 (avoids hls.js init latency)
Bunker Admin
Fixes surfaced by three rounds of fresh-install testing on marcelle:
- config.sh: add host-port preflight check (ss -tln) to catch
cockpit-on-9090 style collisions before compose up; add
--skip-port-check escape hatch; add --install-watcher /
--no-install-watcher / --install-backup-timer /
--no-install-backup-timer flags; -y --enable-all now installs both
systemd units by default (previously silently skipped); print
resolved admin email in Configuration Complete block.
- scripts/validate-env.sh: new section 5b "Host Port Availability"
using ss-based detection, with process-name surfacing when run as
root.
- scripts/pangolin-teardown.sh: new wrapper. Reads credentials from
.env or takes --api-url/--api-key/--org-id flags. Dry-run by
default; --yes to execute. Deletes resources before sites (avoids
orphans). --keep-site-ids for safety.
- scripts/build-release.sh: include validate-env.sh and
pangolin-teardown.sh in release tarball whitelist.
- CCP instances.service.ts: deleteInstance() now calls
teardownTunnel() before composeDown when pangolinSiteId is set.
Previously an admin clicking "Delete Instance" orphaned the
Pangolin site + all its resources. Best-effort with try/catch
matching the existing Docker-cleanup tolerance pattern.
- CLAUDE.md: sync drift — 44 → 50 migrations, 186 → 192 models,
40 → 44 modules.
Bunker Admin
Gitea SSO: cookie-based single sign-on via nginx auth_request — sets
cml_session cookie on login/refresh, validates via /api/auth/gitea-sso-validate,
injects X-WEBAUTH-USER header for reverse proxy auth. Dedicated GITEA_SSO_SECRET
and SERVICE_PASSWORD_SALT env vars isolate secret rotation.
Security fixes from March 30 audit: IDOR on ticketed events (requireEventOwnership
middleware), IDOR on action items (admin/assignee/creator check), path traversal
on photos (resolve-based validation), CSV upload size limit (5MB), shared calendar
email exposure removed.
Gitea provisioner: auto-sync docs repo collaborator access based on role
(CONTENT_ROLES get write, SUPER_ADMIN gets admin). Gitea client extended
with collaborator management API methods.
Production hardening: NODE_ENV defaults to production in docker-compose.prod.yml,
Grafana anonymous auth disabled, install.sh branch ref updated to main.
Admin UI: moved docs reset from toolbar to MkDocs Settings danger zone,
improved collab Ctrl+S to explicitly save + cache-bust preview.
MkDocs site rebuild with updated repo data, upgrade screenshots, and content.
Bunker Admin
- Cookie Secure flag now uses req.secure (respects trust proxy +
X-Forwarded-Proto) instead of NODE_ENV. Works correctly over both
HTTP (local dev) and HTTPS (production tunnel).
- SameSite=Strict over HTTPS, SameSite=Lax over HTTP (browsers reject
Strict cookies over plain HTTP).
- Un-track generated nginx/conf.d/api.conf and services.conf (gitignored,
regenerated from templates at startup).
- Update CLAUDE.md: ENCRYPTION_KEY now required in all environments.
Bunker Admin