23 KiB
23 KiB
Change Log for linearvoid
[Unreleased]
Added
- Transient presence flash: Join/leave announcements now appear as a 3-second flash at the top of the chat panel (
#chat-flash) with a fade-out, instead of permanent chat rows. Presence indicators still update from the same events.
Changed
- System messages no longer persisted: The chat backend no longer stores
system-type messages; chat history and the admin chat-log table only contain real chat messages (moderation actions remain in the audit log). - Reconnect grace period: Disconnects schedule a delayed "left" announcement (5s); a reconnect within 5s cancels it and skips "joined", eliminating join/leave spam on brief reconnects.
- HLS segment duration 2s: Default
HLS_SEGMENT_DURATIONchanged from 6 to 2 andHLS_WINDOW_SIZEfrom 24 to 12 (2s × 12 = 24s live window), reducing end-to-end latency; the 2s GOP was already in place. - Chat font sizing:
.message-textset to0.95rem(slightly smaller than the default, larger than the0.83remsystem/flash text). - R2 retention by run count + size: Playout now keeps the newest
R2_KEEP_RUNSrun folders (default 2) and enforces anR2_MAX_RETENTION_BYTEScap (default 10 GiB), replacing the old 24h time-basedR2_SEGMENT_RETENTION_HOURScleanup. Cleanup runs after each new run plus an hourly backstop, and never deletes the live manifest. Addslinearvoid_playout_r2_runs_retainedandlinearvoid_playout_r2_bytes_retainedgauges. - Cloudflare Workers frontend hosting: Replaced Cloudflare Pages with a Cloudflare Worker (
linearvoid-frontend) that serves the static SPA via Workers Static Assets. Addedfrontend/wrangler.tomland renamed the deploy workflow to.gitea/workflows/workers-deploy.yml(builds the SPA and runsnpx wrangler deploy). Architecture, setup/deployment, and ops documentation updated from Pages to Workers.
Fixed
- Login/register cross-origin API URLs:
submitAuthinfrontend/src/main.tsnow prefixes/api/auth/loginand/api/auth/registerwithVITE_PUBLIC_APP_URL(falling back to same-origin), matching the other API calls so the Workers-served SPA reaches the app origin instead of a same-origin path.
Tests
- WebSocket:
TestHub_Broadcast_SystemNotPersisted,TestHub_ReconnectGracePeriod. - Handlers:
TestHandleAdminChatLog_ExcludesSystem. - Frontend:
presence flashtest. - Playout:
TestCleanupOldRuns,TestCleanupOldRuns_SizeCap,TestR2KeepRuns,TestR2MaxRetentionBytes,TestR2RunFromKey.
[1.0.13] - 2026-08-13
Added
- Automatic media normalization on upload: Chat backend auto-transcodes non-conforming uploads. New
media.NormalizeVideowraps ffmpeg with the Linear Void profile (H.264/AAC, 30fps, 2s keyframes, yuv420p, 48kHz stereo), re-verifies output with ffprobe, and writes*.normalized.mp4. Media records gainnormalizing/normalization_failedstatuses;POST /api/admin/media/{id}/normalizere-triggers manually. Admin UI shows a "Normalize" action with status polling. - R2 segment retention: Playout
cleanupOldSegmentsdeleteshls/r-*/*objects older thanR2_SEGMENT_RETENTION_HOURS(default 24h), run hourly + at startup, never touching the live manifest. Exposeslinearvoid_playout_r2_cleanup_objects_totalmetric..env.exampleaddsR2_SEGMENT_RETENTION_HOURS. - Playout loop metrics:
linearvoid_playout_loops_total(clean completions) andlinearvoid_playout_run_duration_seconds(last run duration). - Runtime configuration store: Thread-safe, JSON-persisted runtime config (
HLS_SEGMENT_DURATION,HLS_WINDOW_SIZE,MATOMO_URL,MATOMO_SITE_ID) with typed getters/setters + validation, editable viaGET/PUT /api/admin/config. Playout reads the sharedconfig.jsonas env fallback. - Matomo analytics: Viewer pageview snippet in
index.htmldriven by the publicGET /api/matomoendpoint. - Chat user accounts: Register/login/logout (
POST /api/auth/register,/login,/logout,GET /api/auth/session), bcrypt password hashing,linearvoid_user_sessioncookie, and a viewer sign-in UI. - Private DM history & search:
GET /api/dm/history(paginated) andGET /api/dm/search, with a DM search UI in the viewer. - Chat UI polish: Typing indicator, user-list toggle, media status badges, and an "Add to playlist" action gated to
readymedia.
Changed
- Playout seamless looping:
main()now only resets HLS output when the playlist changed (SHA-256playlistRevision), on crash/slate recovery, or first run; otherwise it continues segment numbering via-start_numberand prunes stale segments. Eliminates the viewer gap on clean playlist loops. - Single playout entrypoint:
start-playout.shnow runs/app/playlist-manager -validatethenexecs the Go binary, removing the duplicate shell ffmpeg loop. Added a-validatemode tomain(). - Admin media library: Shows status badges (
ready/needs_normalization/normalizing/failed) and only offers "Add to playlist" forreadymedia; playlist entries show per-item status badges. - Prometheus TSDB retention: The
prometheusservice now sets--storage.tsdb.retention.time=30dand--storage.tsdb.retention.size=15GB(in bothdocker-compose.ymlanddocker-compose.prod.yml), so metrics older than 30 days are deleted.
Tests
- Media:
TestNormalizationArgs,TestNormalizeVideo_FFmpegError. - Handlers:
TestNormalizeMediaRecord_Success,TestNormalizeMediaRecord_Failure,TestHandleAdminMediaByID_Normalize. - Playout:
TestCleanupOldSegments,TestR2SegmentRetentionHours,TestPlaylistFingerprint,TestHLSNextStartNumber,TestLoadSharedHLSConfig. - Config:
runtime_test.go(get/set, persist/reload, env fallback, validation). - Auth:
TestUserSessionStore. - Handlers: config GET/PUT + validation, DM history/search, user register/login/session.
[1.0.12] - 2026-08-11
Changed
- Task tracking consolidated: Removed
DEVELOPMENT.mdand moved all development tasks to.github/instructions/BACKLOG.mdas the single source of truth.TODO.mdupdated to referenceBACKLOG.mdandDONE.md.
Fixed
- Playout R2 endpoint validation:
initR2Client()now validates all required R2 environment variables (R2_ENDPOINT,R2_ACCESS_KEY_ID,R2_SECRET_ACCESS_KEY,R2_BUCKET_NAME) before creating the S3 client. Previously, an emptyR2_ENDPOINTcaused crypticCustom endpoint '' was not a valid URIerrors on every segment upload attempt. Playout now gracefully degrades when R2 is not configured — local HLS via nginx continues to work. - CD workflow missing R2 secrets:
.gitea/workflows/cd.yml"Prepare environment" step now injects all R2_ variables (R2_ACCESS_KEY_ID,R2_SECRET_ACCESS_KEY,R2_BUCKET_NAME,R2_ENDPOINT, plus admin variants) from Gitea secrets. Previously the generated.envwas missing R2 entirely, causing the playout container to start with an empty endpoint. - Deployment docs:
docs/DEPLOYMENT.mdnow includes explicit R2 and Cloudflare variable lists plus a "Gitea Secrets" table documenting all 15 required secrets.
[1.0.11] - 2026-08-10
Added
- Phase 1 test fixtures: Media and playlist test fixtures in
backend/playout/testdata/with ffmpeg generation commands documented in test headers. - Phase 4 R2 integration:
SyncToR2now setsCacheControl+ContentTypebased on file extension (immutable segments, short-lived manifests). Three integration tests added. - Phase 2 viewer tests: Presence updates (join/leave/sorted), private DM flow, message rendering (system/self/other) in
main.test.ts(27 total tests). - Phase 3 admin tests: Playlist management (save via PUT, error handling, sortable rendering) in
admin.test.ts(31 total tests passing).
Changed
- Exported
savePlaylistandrenderPlaylistSortablefromadmin.tsfor testing.
[1.0.10] - 2026-08-09
Added
- WebSocket summary helper: Added
scripts/loadtest/summarize-artillery.ps1to extract counters, rates, and available percentile stats from Artillery JSON output. - Playbook worksheet mapping: Added artifact-to-worksheet mapping section in
docs/operations/scaling-performance-playbook.mdfor WebSocket and Admin API benchmark artifacts. - Test fixture generation docs: Added ffmpeg commands for generating valid and invalid media fixtures as comments in
backend/playout/src/playlist_test.go. Phase 1 test fixtures now complete. - R2 cache header support:
SyncToR2now setsCacheControlandContentTypebased on file extension:.m4s/.ts/.mp4/.m4a→ immutable (max-age=31536000).m3u8→ short-lived (max-age=2s)- unknown → no cache headers
- R2 integration tests: Added
TestSyncToR2_ManifestCacheHeadersandTestSyncToR2_NoCacheHeadersForUnknownExtverifying cache header behavior for segment, manifest, and unknown file types.
Changed
- Loadtest runner:
scripts/loadtest/run-loadtests.ps1now generatesws-chat-summary.txtautomatically forwsandallscenarios. - Loadtest docs:
scripts/loadtest/README.mdnow documentssummarize-artillery.ps1usage and the newws-chat-summary.txtartifact. - Planning status wording: Updated
docs/DEVELOPMENT.mdand.github/instructions/TODO.mdto mark ws/admin benchmark scaffolding complete while benchmark execution remains pending. - Phase 1 complete: Test fixture creation and ffmpeg documentation marked done.
- Phase 4 partially complete: R2 upload + cache header validation done; edge cache hit/miss validation remains manual.
- Phase 2 frontend tests: Added presence updates (join/leave rendering), private DM flow, and message rendering tests in
main.test.ts(27 total tests passing). - Phase 3 admin frontend tests: Added playlist management tests (save via PUT, error handling, sortable rendering) in
admin.test.ts(31 total tests passing).
[1.0.9] - 2026-08-06
Added
- Prometheus alerts mount:
alerts.ymlmounted into prometheus container in both dev and prod compose files. - Grafana provisioning: Prometheus datasource + dashboard provider YAML configs in
monitoring/grafana/.
Changed
- Prod prometheus: Switched from empty named volume to bind mounts for config files (consistent with dev).
- CD pipeline: Trigger on
push(parallel with CI), removedenvironment: production(was causing silent abort). - CI pipeline: Removed redundant
releasejob (handled byrelease.yml). - Tag format: Standardized on
*.*.*(e.g.1.0.9), removedvprefix from existing tags. - DEPLOYMENT.md: Documented parallel CI/CD pipeline flow with workflow table.
- Runtime checks: Executed normalization edge-case tests (MOV, MKV, VFR, 5.1 input) and concat continuity verification in running playout environment.
- Incident runbook: Added quarterly disaster recovery drill procedure with prerequisites, outage simulation, restore validation, and evidence template.
- Manual ops checklist: Added
docs/operations/cloudflare-uptimerobot-checklist.mdto execute and record Cloudflare + UptimeRobot dashboard tasks with evidence capture. - Scaling playbook: Added
docs/operations/scaling-performance-playbook.mdwith benchmark methods, trigger thresholds, and Phase 2/3 scaling decision flow.
Fixed
- Prometheus config mount: Switched prod from bind mounts to named volume
prometheus-config, populated viadocker runon remote host. Fixesnot a directoryerror when bind source path doesn't exist on deployment host. - Viewer mobile overflow: Replaced
100vwwith100%on full-screen viewer containers to remove horizontal overflow on mobile viewports.
Removed
.github/MEMORY.md,.github/agents/,.github/instructions/— tracking files migrated out of repo.
[1.0.8] - 2026-08-06
Changed
- API.md: Added full HTTP status code matrix (table with 12 codes) and rate limit headers documentation.
- DEPLOYMENT.md: Added troubleshooting table (8 common symptoms with check/fix).
- MONITORING.md: Full rewrite with architecture diagram, 7 scrape targets, 8 alert rules, key metrics, troubleshooting.
- SECURITY.md: Full rewrite with threat model diagram, TLS/HSTS config, network isolation, secrets management, incident response.
- Architecture docs: Filled Level 2 building blocks (Mermaid diagram, 5 subsystems with code locations) and Level 3 module breakdowns (chat: 9 modules, playout: 6 modules, frontend: 10 modules).
[1.0.7] - 2026-08-06
Added
- Emergency slate fallback:
scripts/generate-slate.shcreates "We'll Be Right Back" slate video + pre-rendered HLS. Playout detects FFmpeg failures (>5 restarts/60s) and auto-switches to slate manifest. Health endpoint reportsdegradedmode. Auto-recovers when FFmpeg restarts successfully. - Prometheus metrics:
linearvoid_playout_restarts_total,linearvoid_playout_mode(0=live, 1=slate),linearvoid_chat_dms_totalexposed on playout + chat/metrics. - Video normalization pipeline:
scripts/normalize.shwith env-configurable CRF/preset/audio-bitrate wrapping reference FFmpeg command. - Strict ffprobe verification gate:
ValidateVideo()in playout parses ffprobe JSON, checks codec (h264/aac), framerate (30/1), resolution (1280x720 or 1920x1080), pix_fmt (yuv420p), sample_rate (48000), keyframe interval (≤2s).ValidateVideoStrict()returns{valid, errors[]}. - Upload normalization enforcement:
validateMediaNormalization()runs on every admin upload (single + chunked). Non-conforming files getneeds_normalizationstatus. Visible in media library + playlist validation warnings. - Chat history on room join:
pushChatHistory()sends last 50 messages via WebSocket on connect. - DM offline delivery: Private messages persisted to DB, appear in history when recipient reconnects.
- Online presence indicators: Green dot per user in chat sidebar, updated via join/leave system messages.
- Schedule metadata overlay: "Now Playing" + "Up Next" below viewer player, polling
GET /api/scheduleevery 30s. Public/api/scheduleendpoint added. - Playlist drag-to-reorder: Sortable list with drag-and-drop, per-entry remove buttons, validation warnings (missing files, needs_normalization).
- Schedule overrides:
ScheduleOverridemodel +GET/POST/DELETE /api/admin/schedule/overrideswith conflict detection (409 on overlap). Admin UI with form + list. - User management actions:
POST /api/admin/userswith mute/unmute/ban/unban actions. Per-user action buttons in admin users table. - Inline moderation: Mute/Ban buttons on each chat log entry row in admin.
- CSV export:
GET /api/admin/chat-log/exportreturns CSV download. - Analytics time range selector: 1h, 6h, 24h, 7d dropdown on admin analytics page.
- Runtime config editor:
GET/PUT /api/admin/configfor rate limit max + window. Admin system page with config form + restart buttons. - Production target documentation:
DEPLOYMENT.mdupdated with two-environment model (test lab 192.168.88.0/24 vs Hetzner production TBD), CX22 specs, firewall requirements, DNS cutover plan. - Network diagram: Mermaid diagram in
SETUP.mddistinguishing test lab from planned production. - E2E test script:
scripts/test-playout-e2e.shfor runtime validation in test lab. - Redis pub/sub stub: Architecture ready for multi-instance chat (pending implementation).
Changed
docker-compose.yml+.prod.yml: added slate volume mount (./data/slate:/app/slate:ro), Redis--maxmemory 256mb --maxmemory-policy allkeys-lru.backend/playout/Dockerfile: added/app/slatedirectory.handleConnection: added 30s presence heartbeat + WebSocket ping, banned/muted user checks, env-configurable rate limits (CHAT_RATE_LIMIT_MAX,CHAT_RATE_LIMIT_WINDOW_S).handleModerationCommand: added moderator role check, mute duration parsing, Redis key enforcement.handlePrivateDM: targeted delivery viaclientMetamap instead of broadcast.initRedis: addedredisReconnectLoopwith exponential backoff (2s→60s).handleAdminMedia+ chunked upload: normalization validation runs on every upload, setsneeds_normalizationstatus.- nginx: OPTIONS preflight → 204 on all 4 HLS locations.
/healthreturns JSON./nginx-statuswith stub_status (internal-network restricted). - CD workflow: health checks now IP-agnostic (
docker execinstead of hardcoded IP curls). docker-compose.yml+.prod.yml,.gitea/workflows/cd.yml,DEPLOYMENT.md,SETUP.md,docs/architecture/README.md,README.md: all annotated with test-lab vs production distinction,192.168.88.0/24flagged as internal test network.docs/architecture/README.md: CI/CD deployment view updated to note test lab target, production pending.
Fixed
- CD health check no longer hardcodes
192.168.88.40in curl targets — usesdocker execfor portability. - Playlist page no longer single-textarea — now shows sortable list with validation warnings.
- Schedule page shows overrides alongside playlist timeline.
[1.0.6] - 2026-08-05
Added
GET /api/admin/chat-logendpoint with?room=,?limit=,?before=,?username=,?text=filter/pagination support.GET /api/admin/chat-log/:idendpoint returning a single chat log entry as JSON.DELETE /api/admin/chat-log/:idendpoint returning 204 No Content on success.DELETE /api/admin/chat-log/bulkendpoint accepting{"ids": [...]}for bulk deletion (max 500).- Chat Log admin tab with table, individual delete with confirmation dialog, pagination (prev/next), search/filter by room/username/text, select-all checkbox, and bulk delete button.
- Added
"lint": "tsc --noEmit"script tofrontend/package.json. /api/admin/media/{id}GET/PUT/DELETE handlers for media metadata retrieval, updates, and file+record deletion.- Resumable chunked admin media upload flow with upload-session endpoints and client progress indicators.
- Prometheus-format
/metricsendpoint for chat runtime and admin-session monitoring. - Dedicated operational documentation:
API.md,DEPLOYMENT.md,MONITORING.md,SECURITY.md.
Changed
- Split
frontend/admin/index.htmlinto a template shell plus 9 per-tab page HTML files loaded dynamically vialoadPageContent/switchTab/bindPageEvents. - Rebuilt viewer, admin, and admin-login pages around custom semantic class structures with accessibility-focused landmarks, tab semantics, and keyboard-focus behavior.
- Replaced legacy and Tailwind-based stylesheet rules with a custom design-token CSS system covering responsive layouts, forms, tables, and reduced-motion behavior.
- Switched frontend theme to dark-mode-first token defaults with light-scheme override.
- Applied new color palette (
#32292fdark,#b4c4aelight,#587792brand,#2ebfa5focus,#04650dok) across all 22 CSS custom properties in dark and light themes. - Replaced hardcoded admin sidebar and tab-trigger colors with CSS custom properties.
- Optimized CI pipeline: removed duplicate
docker compose build --no-cachefrom test job, removed broken playlist validation step, fixed Go module cache path, added Docker Buildx registry cache to docker/release jobs. - Bumped chat healthcheck
start_periodfrom 10s to 30s indocker-compose.prod.yml. - CD workflow now verifies nginx (port 8080) + chat (3000) + playout (8081); dumps container logs for all failing services.
- Updated Prometheus scrape targets and Grafana dashboard queries to use live chat metrics.
Fixed
- Go module cache in CI now correctly references only
backend/chat/go.sum(playout go.sum did not exist); addedgo mod downloadstep to seed cache. - Removed broken playlist validation step (
go run src/playlist.gowithout go.mod) from CI test job. - Converted nginx CI build from custom shell
docker buildxtodocker/build-push-action@v5for cache consistency. - Enforced 2GB upload ceiling plus server-side media file type/size validation for admin uploads.
- Increased nginx
client_max_body_sizeto allow resumable admin upload chunks without 413 errors. - Removed unused
data-admin-*Vite env var attributes from admin HTML template. - Removed Tailwind and Alpine runtime/build dependencies; eliminated Tailwind directives/warnings during production build.
[1.0.3] - 2026-08-03
Added
- Added Prometheus-format
/metricsendpoint for chat runtime and admin-session monitoring.
Changed
- Updated Prometheus scrape targets and Grafana dashboard queries to use live chat metrics.
Fixed
- Fixed nginx routing so
/metricsis reachable through localhost andlinearvoid.tv.
[1.0.2] - 2026-08-03
Changed
- Updated local environment documentation and examples to prefer real-domain local routing through
linearvoid.tvand compose-safe internal service hostnames.
Fixed
- Fixed admin production build output and nginx routing so
/admin/serves its own entrypoint. - Fixed static asset serving so hashed JS/CSS files return correct MIME types and stale asset URLs return
404instead of SPA HTML. - Fixed chat database configuration so
DATABASE_URLis honored for compose-safe Postgres or SQLite local development.
[1.0.1] - 2026-08-03
Changed
- Expanded the setup guide with current
.env.examplevariables, development prerequisites, Cloudflare R2 setup, Gitea registry setup, and a production deployment guide. - Aligned frontend, nginx, and deployment docs with the
linearvoid.tvapp domain andmedia.linearvoid.tvHLS domain model.
[1.0.0] - 2026-08-02
Added
Full backend and frontend implementation complete.
- Playout Service (
backend/playout/): Go-based playlist management, ffprobe normalization validation, continuous HLS with immutable segment naming, automatic R2 sync, health check endpoint, and Docker integration (ADR-001, ADR-003). - Chat Backend (
backend/chat/): Go + Gorilla WebSocket, Redis for presence/rate limiting/pub/sub, SQLite persistence for message history and ModerationLog (ADR-006/ADR-007), public rooms, private DM routing, moderation commands with immutable audit logging, health check endpoint, and docker-compose integration. - Frontend (
frontend/): Vite + TypeScript + Tailwind + Alpine.js, HLS player with hls.js, chat sidebar with room switching and private messaging, "Now Playing" overlay, and modern UI (ADR-002, ADR-003). - Complete
docker-compose.ymlfor local development with all services, volumes, and healthchecks. - All ADRs (001-007) created and linked in documentation.
CHANGELOG.mdupdated with full traceability.- Git repository initialized with comprehensive initial commit.
The project is a fully functional, self-hosted 24/7 linear video channel with synchronized playback, custom chat, R2 delivery, moderation, and admin-ready structure. It matches the architecture defined in the arc42 template and all ADRs.
Next Steps (Future ADRs):
- ADR-008: Admin Interface Implementation (ADR-004/ADR-005).
- ADR-009: CI/CD, monitoring (Prometheus/Grafana), and production deployment.
- Tests, admin UI, and final polish.
See docs/architecture/README.md and the ADR files for complete documentation. The system is ready for use and further extension.