Files
linearvoid/README.md
T
zwitschi 68bd241f2c
Release / release (push) Successful in 29s
CI / test (push) Successful in 53s
CI / docker (push) Successful in 22s
CD / deploy (push) Failing after 10s
fix: use named volume for prometheus config, populate on remote host
2026-08-06 22:13:24 +02:00

15 KiB
Raw Permalink Blame History

Linear Void

Linear Void is a self-managed 24/7 linear video channel for a primarily US audience. It runs a continuous playlist of self-owned videos with true/near-true synchronization, a custom lightweight viewer SPA (hls.js player + chat), and full independence from third-party platforms (YouTube, Twitch, etc.).

Repository: git.allucanget.biz/zwitschi/linearvoid

Build Status License Gitea Version Release Notes

Architecture

The architecture (detailed in docs/architecture/README.md) uses:

  • Hetzner Ashburn VPS for playout (FFmpeg + MediaMTX/SRS) and chat (planned production; currently test lab at 192.168.88.40).
  • Cloudflare R2 + CDN with aggressive immutable caching of HLS .ts segments ($0 egress).
  • Lightweight static frontend and WebSocket chat backend.
  • Gitea self-hosted CI/CD (Actions) and Container Registry.

See docs/architecture/adr/ for the full set of decision records (ADR-001 through ADR-007) covering architecture, storage calculations, cache rules, cost model, module structure, tech stack, admin interfaces, data storage, and caching strategy.

Key Features

  • Continuous synchronized playback from a playlist (standard HLS, 15–35s latency).
  • Self-hosted custom SPA with embedded chat (public rooms + private 1:1 DMs, moderation).
  • Extreme cost efficiency via Cloudflare R2 edge caching.
  • Video normalization, monitoring, fallback slate, and easy playlist management.
  • US-centric performance (Ashburn origin + Cloudflare POPs).

Architecture Diagram

flowchart TD
    V[US Viewers] -->|HTTPS| CF[Cloudflare CDN]
    CF -->|Cache HIT| R2[Cloudflare R2]
    CF -->|Cache MISS| N[Nginx :80]
    N -->|/ws| CH[Chat :3000]
    N -->|/admin| CH
    N -->|/hls| P[Playout :8081]
    N -->|/*| FE[Frontend SPA]
    CH --> R[(Redis)]
    CH --> DB[(PostgreSQL)]
    P -->|ffmpeg| HLS[/data/hls/]
    P -->|sync| R2

Project Structure

linearvoid/
├── frontend/                  # Static Single-Page Application (HTML/CSS/JS)
│   ├── src/
│   │   ├── components/        # Video player, chat sidebar, now-playing overlay
│   │   ├── js/                # hls.js integration, WebSocket client, UI logic
│   │   └── styles/            # Tailwind / custom CSS
│   ├── index.html
│   └── package.json           # Build tools (Vite recommended)
├── backend/
│   ├── playout/               # Continuous HLS generation from playlist
│   │   ├── src/               # FFmpeg orchestration, playlist manager
│   │   ├── config/
│   │   └── Dockerfile
│   ├── chat/                  # Real-time WebSocket service
│   │   ├── src/               # Rooms, DMs, moderation, presence (Node/Go)
│   │   ├── config/
│   │   └── Dockerfile
│   └── shared/                # Common utilities, types, normalization helpers
├── infrastructure/            # Deployment, IaC, Cloudflare configs
│   ├── docker-compose.yml     # Local/dev orchestration
│   ├── nginx/                 # Proxy, static serving, HLS delivery
│   ├── cloudflare/            # Cache rule examples, Workers
│   └── terraform/             # (optional) provisioning
├── scripts/                   # Automation scripts
│   ├── normalize-video.sh     # Pre-process MP4s (codec, keyframes, CRF)
│   ├── sync-to-r2.sh          # Upload segments/manifests to R2
│   └── backup.sh
├── monitoring/                # Dashboards, alerts, logging
│   ├── grafana/
│   └── prometheus/
├── docs/
│   └── architecture/
│       ├── README.md          # arc42 template (fully populated)
│       └── adr/
│           ├── ADR-001.md             # Consolidated decision record
│           ├── ADR-002-module-structure.md
│           ├── ADR-003-tech-stack.md
│           ├── ADR-004-admin-video.md
│           ├── ADR-005-admin-moderation-analytics.md
│           ├── ADR-006-data-storage.md
│           └── ADR-007-redis-usage.md
├── .env.example
├── .gitignore
├── docker-compose.yml         # Top-level for dev (playout + chat + nginx)
├── README.md
└── LICENSE

Folder Explanations

  • frontend/: The viewer experience — minimal, modern SPA with Tailwind + Alpine.js (or vanilla). Uses hls.js for playback and a WebSocket client for chat.
  • backend/playout/: Core linear engine. FFmpeg consumes a normalized playlist and outputs continuous HLS. Includes normalization validation.
  • backend/chat/: Independent WebSocket service (Node.js/Socket.io or Go). Handles public rooms, private DMs, presence (Redis), persistence (PostgreSQL/SQLite), rate limiting, and moderation.
  • backend/shared/: Reusable code (types, utils, video validation).
  • infrastructure/: Everything needed to deploy and configure the stack (Docker, Nginx, Cloudflare rules, future Terraform).
  • scripts/: One-off and CI-friendly scripts for video preparation, R2 sync, backups, and playlist management.
  • monitoring/: Grafana dashboards, Prometheus rules, logging configuration.
  • docs/architecture/: Complete arc42 documentation + ADRs 001-007 (the single source of truth for all architectural decisions).

This structure directly reflects the building blocks, runtime flows, deployment view, and cross-cutting concepts defined in the arc42 documentation.

Getting Started

Note: The docker compose up below targets local development. The test lab (192.168.88.40) receives automated CD deploys. Production Hetzner VPS is not yet provisioned — see DEPLOYMENT.md.

git clone https://git.allucanget.biz/zwitschi/linearvoid.git
cd linearvoid
cp .env.example .env
# Edit .env with your R2 credentials and domain settings
docker compose up --build

Then open:

Full setup guide: SETUP.md.

See the architecture docs for detailed architecture and ADRs.

Development

  • Use Docker Compose for local development (playout + chat + frontend).
  • Test lab host: 192.168.88.40 (internal network, receives CD deploys). Production Hetzner VPS TBD.
  • Public production host model: https://linearvoid.tv for viewer/admin/chat and https://media.linearvoid.tv for HLS delivery.
  • Normalize all source videos before adding to playlist (see ADR-001 for exact codec/fps/keyframe requirements).
  • Monitor cache hit ratio and R2 operations closely — they determine real cost.

Environment Variables

Variable Default Description
R2_ACCESS_KEY_ID — Cloudflare R2 access key
R2_SECRET_ACCESS_KEY — Cloudflare R2 secret key
R2_BUCKET_NAME linearvoid-media R2 bucket for HLS segments
DATABASE_URL postgres://...@db:5432/... Chat DB (Postgres or SQLite)
REDIS_URL redis://redis:6379/0 Redis for presence/rate limiting
VITE_PUBLIC_APP_URL https://linearvoid.tv Public viewer URL
VITE_PUBLIC_WS_URL wss://linearvoid.tv/ws WebSocket endpoint
VITE_PUBLIC_HLS_URL https://media.linearvoid.tv/hls/playlist.m3u8 HLS playlist URL
ADMIN_USERNAME admin Admin panel username
ADMIN_PASSWORD — Admin panel password
HLS_SEGMENT_DURATION 6 HLS segment duration in seconds
HLS_WINDOW_SIZE 24 HLS playlist window size

Full reference: .env.example.

Troubleshooting

Problem Check
Playout not starting Verify playlist exists in data/playlist/playlist.txt and media files are accessible
HLS not playing Check docker compose logs playout for ffmpeg errors; verify data/hls/ permissions
Chat not connecting Ensure Redis is running: docker compose logs redis; check DATABASE_URL
Admin panel 401 Verify ADMIN_USERNAME/ADMIN_PASSWORD in .env match login credentials
Docker build fails Run docker compose build --no-cache; check backend/playout/.dockerignore
R2 upload failures Verify R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY; check bucket CORS settings

API Endpoints

Service Endpoint Description
Chat GET /health Health check
Chat GET /metrics Prometheus metrics
Chat GET /ws WebSocket upgrade for chat
Chat GET /api/admin/* Admin API (auth required)
Chat POST /api/admin/login Admin login
Playout GET /health Health check (port 8081)
Nginx GET / Viewer SPA
Nginx GET /admin/ Admin panel
Nginx GET /hls/playlist.m3u8 HLS manifest

Architecture Documentation

The architecture is fully documented using the arc42 template (with populated sections, Mermaid context and building-block diagrams, deployment view, cross-cutting concepts, quality goals, risks, and glossary).

Decision Records (in docs/architecture/adr/):

  • ADR-001: Overall system architecture, HLS + Cloudflare R2 caching strategy, storage calculations, cost model, risks, and implementation plan.
  • ADR-002: Chosen project folder/module structure (frontend, backend/playout, backend/chat, infrastructure, scripts, monitoring) and mapping to arc42 building blocks.
  • ADR-003: Technology stack, language choices per module (TypeScript for frontend, Go preferred for chat and playout wrapper, Bash/Python for scripts), Docker-first approach, and all implicit assumptions.
  • ADR-004: Video admin web interface (upload, playlist management, schedule overrides, normalization enforcement).
  • ADR-005: Web admin interfaces for user management, moderation dashboard, analytics, and system configuration.
  • ADR-006: Data storage strategy (SQLite vs PostgreSQL for v1, migration path, admin interface capabilities).
  • ADR-007: Redis usage (exact use cases, TTL policies, persistence, fallback behavior, admin configuration).

See the architecture README for the big-picture view and the individual ADR files for detailed rationale, alternatives, and consequences.

Contributions, issues, and further evolution (LL-HLS, ABR, multi-channel, E2EE) should reference the architecture documentation.

Resources

  • CHANGELOG.md - Version history and completed features.
  • CONTRIBUTING.md - Guidelines for contributing to the project.
  • SETUP.md - Complete setup guide with prerequisites, environment configuration, running the application, and troubleshooting.
  • API.md - Implemented WebSocket and REST API reference.
  • DEPLOYMENT.md - Production deployment and operations guide.
  • MONITORING.md - Prometheus and Grafana setup and troubleshooting.
  • SECURITY.md - Security controls, hardening checklist, and incident baseline.
  • Architecture Documentation - Full arc42 template with diagrams and decisions.
  • ADRs - Detailed decision records (ADR-001 to ADR-007).

CI/CD: View workflows on Gitea Actions.

Container Registry: git.allucanget.biz/zwitschi/linearvoid/<service>:latest

Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines. Quick overview:

  1. Read the architecture docs and ADRs first.
  2. Code against the defined module structure (frontend, backend/playout, backend/chat, infrastructure, scripts, monitoring).
  3. Open PRs on Gitea.
  4. CI validates builds and tests automatically.

Since this a linear project, we also rely on a linear development and release model: Only main branch is used for releases, and all features are merged into main after review and CI validation. Releases are tagged with semantic versioning (1.0.3, 1.0.4, etc.) and automatically built/pushed to the container registry. Version numbers are declared in frontend/package.json, CHANGELOG.md, and the version badge in this README. For patches, we use even numbers for stable versions (1.0.2, 1.0.4) and odd numbers for pre-release/unstable versions (1.0.3, 1.0.5).

MIT License

The project is ready for use. See SETUP.md for detailed installation instructions.