zwitschi f9aba11b90
CD / deploy (push) Successful in 57s
CI / test (push) Failing after 1m29s
CI / docker (push) Skipped
feat: add metrics, observability, test fixtures, and scaling playbook artifacts
2026-08-10 00:23:50 +02:00
2026-08-06 21:39:25 +02:00
2026-08-07 16:04:00 +02:00
2026-08-07 16:04:00 +02:00

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 .m4s 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.
  • Scaling Playbook - Performance test method, thresholds, and scaling decision flow.
  • 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 is 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 use semantic version tags in *.*.* format (for example 1.0.9) and are automatically built/pushed to the container registry. Version numbers are declared in frontend/package.json, CHANGELOG.md, and the version badge in this README.

MIT License

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

S
Description
No description provided
Readme MIT
6.4 MiB
0 Stars 1 Watchers 0 Forks
1.1.0
Latest
2026-09-03 09:27:38 +02:00
Languages
Go 57.3%
TypeScript 19.5%
Shell 7%
HTML 4.7%
PowerShell 4.2%
Other 7.3%