zwitschi d8cf853842
Release / release (push) Successful in 1m15s
CI / test (push) Successful in 2m53s
CI / docker (push) Successful in 2m0s
chore(release): 1.1.0
- playout: run-scoped R2 manifest URIs (candidate a rewrite via rewriteManifestToRun)
- tests: playlist_rewrite_test.go (rewrite + publish integration + slate guard)
- docs: STREAMING, cloudflare-workers-setup, architecture (incl. Level-2 diagram)
- release housekeeping: TODO->DONE, CHANGELOG 1.1.0, version bump
2026-09-03 09:27:38 +02:00
2026-09-03 09:27:38 +02:00
2026-09-03 09:27:38 +02:00
2026-09-03 09:27:38 +02:00
2026-09-03 09:27:38 +02:00
2026-09-03 09:27:38 +02:00
2026-09-03 09:27:38 +02:00
2026-09-03 09:27:38 +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 Gitea License 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

A detailed description of the Streaming architecture, playout, and delivery is available in docs/STREAMING.md.

Project Structure

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

linearvoid/
├── docs/                   # see Documentation section below
├── backend/                # see Backend section below
├── frontend/               # see Frontend section below
├── infrastructure/         # see Infrastructure section below
├── monitoring/             # see Monitoring section below
├── scripts/                # see Scripts section below
├── .editorconfig           # EditorConfig for consistent coding styles
├── .env.example            # Example environment variables for local development
├── .gitignore              # Ignore node_modules, build artifacts, etc.
├── .prettierignore         # Ignore files for Prettier formatting
├── CHANGELOG.md            # Release notes and version history
├── CONTRIBUTING.md         # Contribution guidelines
├── docker-compose.prod.yml # Production deployment (Hetzner + Cloudflare)
├── docker-compose.yml      # Top-level for dev (playout + chat + nginx)
└── README.md               # This file

Documentation

  • docs/: Folder containing all documentation for the project, including architecture, ADRs, operational procedures, and setup guides.
  • docs/architecture/: Architecture documentation, including high-level system architecture, component interactions, and decision records (ADRs).

see README.md for full architecture, ADRs, and operational documentation.

Backend

  • 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).
backend/
├── chat/                               # WebSocket chat service (Node.js or Go)
│   ├── src/                            # Chat service source code (rooms, DMs, moderation)
|   |   ├── auth/                       # Admin authentication and session management
|   |   |   ├── auth_test.go            # Unit tests for authentication
|   |   |   └── auth.go                 # Authentication logic (login, session validation)
|   |   ├── config/                     # Configuration files for chat service
|   |   |   ├── config_test.go          # Unit tests for configuration
|   |   |   └── config.go               # Configuration loading and validation
|   |   ├── db/                         # Database access and models
|   |   |   ├── db_test.go              # Unit tests for database access
|   |   |   ├── db.go                   # Database connection and query logic
|   |   |   ├── models.go               # Database models (users, messages, rooms)
|   |   |   └── redis.go                # Redis connection and presence management
|   |   ├── handlers/                   # WebSocket message handlers
|   |   |   ├── handlers_test.go        # Unit tests for WebSocket message handlers
|   |   |   └── handlers.go             # WebSocket message handling logic
|   |   ├── media/                      # Media-related utilities (e.g., video validation)
|   |   |   ├── media_test.go           # Unit tests for media-related utilities
|   |   |   └── media.go                # Media-related utility functions
|   |   ├── metrics/                    # Prometheus metrics endpoints
|   |   |   ├── metrics_test.go         # Unit tests for Prometheus metrics
|   |   |   └── metrics.go              # Prometheus metrics collection and exposition
|   |   ├── ratelimit/                  # Rate limiting logic (Redis-based)
|   |   |   ├── ratelimit_test.go       # Unit tests for rate limiting
|   |   |   └── ratelimit.go            # Rate limiting logic using Redis
|   |   ├── ws/                         # WebSocket handlers
|   |   |   ├── ws_test.go              # Unit tests for WebSocket handlers
|   |   |   └── ws.go                   # WebSocket connection and message handling
|   |   └── main.go                     # Entry point for chat service
|   ├── Dockerfile                      # Dockerfile for chat service
|   ├── go.mod                          # Go module file for chat service
|   └── go.sum                          # Go checksum file for chat service
└── playout/                            # Continuous HLS generation from playlist (Go)
    ├── demodata/                       # Sample media and playlist files for testing
    |   ├── media/                      # Sample media files for testing
    |   └── playlist/                   # Sample playlist files for testing
    ├── src/                            # Playout service source code (FFmpeg orchestration)
    |   ├── go.mod                      # Go module file for playout service
    |   ├── go.sum                      # Go checksum file for playout service
    |   ├── playlist_test.go            # Unit tests for playlist management
    |   └── playlist.go                 # Playlist management logic
    └── testdata/                       # Test data for playout service (sample playlists, media)
    |   ├── comments_only.txt           # Sample playlist with comments only
    |   ├── empty_playlist.txt          # Sample empty playlist
    |   ├── invalid_24fps.mp4           # Sample invalid 24fps video for testing
    |   ├── invalid_h265.mp4            # Sample invalid H.265 video for testing
    |   ├── mixed_valid_invalid.txt     # Sample playlist with mixed valid and invalid entries
    |   ├── valid_720p30.mp4            # Sample valid 720p30 video for testing
    |   └── valid_playlist.txt          # Sample valid playlist for testing
    ├── Dockerfile                      # Dockerfile for the playout service
    └── start-playout.sh                # Script to start the playout service

Frontend

The viewer experience — minimal, modern SPA with custom CSS + vanilla JS. Uses hls.js for playback and a WebSocket client for chat.

frontend/
├── admin/                  # Admin SPA (playlist management, moderation, analytics)
│   ├── login/              # Login page for admin authentication
|   |  └── index.html       # Admin login page
|   └── index.html          # Admin SPA
├── public/                 # Static assets (favicon, images, etc.)
│   ├── admin/              # Admin static assets
|   |  ├── analytics.html   # Analytics dashboard page
|   |  ├── chat-log.html    # Chat log viewer page
|   |  ├── dashboard.html   # Admin dashboard page
|   |  ├── moderation.html  # User moderation page
|   |  ├── playlist.html    # Playlist management page
|   |  ├── schedule.html    # Schedule overrides page
|   |  ├── system.html      # System configuration page
|   |  ├── upload.html      # Video upload page
|   |  └── users.html       # User management page
│   ├── favicon.ico         # Favicon for the SPA
|   └── icons.svg           # SVG icons for the SPA
├── src/                    # Source code for the SPA
│   ├── assets/             # Images, fonts, and other static assets
|   |  ├── hero.png         # Hero image for the SPA
|   |  ├── typescript.svg   # TypeScript logo for the SPA
|   |  └── vite.svg         # Vite logo for the SPA
│   ├── admin.test.ts       # Admin frontend tests (Vitest)
│   ├── admin.ts            # Admin SPA logic (playlist, moderation, analytics)
│   ├── counter.ts          # Counter component for testing
│   ├── main.test.ts        # Main frontend tests (Vitest)
│   ├── main.ts             # Main SPA logic (hls.js player, WebSocket chat)
│   └── style.css           # Custom CSS for the SPA
├── .gitignore              # Ignore node_modules, build artifacts, etc.
├── index.html              # Main SPA entry point
├── package-lock.json       # Lockfile for npm dependencies
├── package.json            # Build tools (Vite recommended)
├── tsconfig.json           # TypeScript configuration
├── vite.config.ts          # Vite configuration
└── vitest.config.ts        # Vitest configuration

Infrastructure

Everything needed to deploy and configure the stack (Docker, Nginx, Cloudflare rules, future Terraform).

Monitoring

Grafana dashboards, Prometheus rules, logging configuration.

  • Grafana
    • dashboards
      • dashboard.json — operational dashboards for playout, chat, and Redis
      • dashboard.yml — operational dashboards for playout, chat, and Redis
    • datasources
    • dashboard.json — operational dashboards for playout, chat, and Redis
  • Prometheus
    • alerts.yml — alerting rules for playout, chat, Redis, and system health
    • prometheus.yml — scrape targets for playout, chat, Redis, and system metrics

Scripts

One-off and CI-friendly scripts for video preparation, R2 sync, backups, and playlist management.

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 2 HLS segment duration in seconds
HLS_WINDOW_SIZE 12 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%