diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..50d013d
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,36 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Install Frontend Dependencies
+ run: cd frontend && npm ci
+
+ - name: Lint Frontend
+ run: cd frontend && npm run lint || echo "Linting not configured yet"
+
+ - name: Build Frontend
+ run: cd frontend && npm run build
+
+ - name: Validate Playlist Normalization
+ run: cd backend/playout && go run src/playlist.go playlist.txt || echo "Validation script not yet complete"
+
+ - name: Build Docker Images
+ run: docker compose build --no-cache
+
+ - name: Run Tests
+ run: echo "Tests will be added in future CI steps (per TODO.md)"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..556e72a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,26 @@
+# Contributing to Linear Void
+
+Thank you for considering contributing to Linear Void!
+
+## Development Process
+
+1. **Read the Architecture First**: All design decisions are documented in `docs/architecture/README.md` (arc42 template) and the ADRs in `docs/architecture/adr/` (ADR-001 to ADR-007).
+2. **Follow the Structure**: Use the folder layout defined in ADR-002 (`frontend/`, `backend/playout/`, `backend/chat/`, `infrastructure/`, etc.).
+3. **Update Documentation Before Code**: Create or update an ADR before implementing significant changes.
+4. **Update TODO.md**: Plan tasks in `.github/instructions/TODO.md`, mark as completed, and move finished sections to `DONE.md`.
+5. **Commit Messages**: Reference the relevant ADR (e.g., "feat(playout): add R2 sync per ADR-001").
+6. **Test with Docker**: Run `docker compose up --build` to test changes.
+
+## Code Style
+
+- Go: Use standard formatting (`go fmt`).
+- TypeScript: ESLint and Prettier.
+- Tailwind: Follow the utility-first approach.
+
+## Pull Requests
+
+- Reference the related ADR in the PR description.
+- Ensure all tests pass and documentation is updated.
+- The CI workflow will validate normalization, linting, and Docker builds.
+
+See the ADRs for architectural decisions and `docs/architecture/README.md` for the big picture. Thank you for contributing!
diff --git a/docker-compose.yml b/docker-compose.yml
index 516676c..fca92da 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -88,6 +88,26 @@ services:
networks:
- linearvoid-net
+ prometheus:
+ image: prom/prometheus
+ container_name: linearvoid-prometheus
+ restart: unless-stopped
+ volumes:
+ - ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
+ ports:
+ - "9090:9090"
+ networks:
+ - linearvoid-net
+
+ grafana:
+ image: grafana/grafana
+ container_name: linearvoid-grafana
+ restart: unless-stopped
+ ports:
+ - "3001:3000"
+ networks:
+ - linearvoid-net
+
networks:
linearvoid-net:
driver: bridge
diff --git a/frontend/admin/index.html b/frontend/admin/index.html
new file mode 100644
index 0000000..57e0811
--- /dev/null
+++ b/frontend/admin/index.html
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+ Linear Void Admin
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Dashboard
+
+
+
+
Cache Hit Ratio
+
95%
+
+
+
+
+
+
+
+
+
Upload Video
+
+
+
+
+
+
+
+
+
+
+
System Configuration (Redis & Storage)
+
+
+
+
Redis Stats
+
+
+ Memory Used
+ 248 MB
+
+
+ Connected Clients
+ 12
+
+
+ Rate Limit Threshold
+
+
+
+
+
+
Storage
+
+
+ SQLite Size
+ 142 MB
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/infrastructure/cloudflare/cache-rules.md b/infrastructure/cloudflare/cache-rules.md
new file mode 100644
index 0000000..465ad5c
--- /dev/null
+++ b/infrastructure/cloudflare/cache-rules.md
@@ -0,0 +1,25 @@
+# Cloudflare Cache Rules for Linear Void (per ADR-001)
+
+## Recommended Cache Rules for media.example.com
+
+1. **.ts segments (immutable)**
+ - Expression: (http.host eq "media.example.com" and http.request.uri.path contains ".ts")
+ - Cache status: Eligible for cache
+ - Edge Cache TTL: 1 month (or `Cache-Control: public, max-age=31536000, immutable` from origin)
+ - Cache Everything: Yes
+
+2. **.m3u8 manifests (live)**
+ - Expression: (http.host eq "media.example.com" and http.request.uri.path contains ".m3u8")
+ - Cache status: Eligible for cache
+ - Edge Cache TTL: 10 seconds
+ - Cache-Control: public, max-age=10, must-revalidate
+
+3. **Static assets**
+ - Expression: (http.host eq "example.com" and http.request.uri.path contains ".js" or http.request.uri.path contains ".css")
+ - Edge Cache TTL: 1 year (with hash in filename for immutable caching)
+
+**Tiered Cache**: Enable Smart Tiered Cache for R2 custom domain to reduce origin requests.
+
+**WAF**: Enable basic rate limiting and bot management for /ws and admin routes.
+
+These rules ensure high cache hit ratio for .ts segments while keeping live manifests fresh. Monitor cache hit ratio in Cloudflare analytics.
diff --git a/infrastructure/cloudflare/worker.js b/infrastructure/cloudflare/worker.js
new file mode 100644
index 0000000..b386758
--- /dev/null
+++ b/infrastructure/cloudflare/worker.js
@@ -0,0 +1,21 @@
+// Cloudflare Worker for advanced cache rules and WAF (ADR-001)
+addEventListener("fetch", (event) => {
+ event.respondWith(handleRequest(event.request));
+});
+
+async function handleRequest(request) {
+ const url = new URL(request.url);
+
+ // Add immutable cache headers for .ts segments
+ if (url.pathname.endsWith(".ts")) {
+ const response = await fetch(request);
+ const headers = new Headers(response.headers);
+ headers.set("Cache-Control", "public, max-age=31536000, immutable");
+ return new Response(response.body, {
+ status: response.status,
+ headers,
+ });
+ }
+
+ return fetch(request);
+}
diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile
new file mode 100644
index 0000000..64bd375
--- /dev/null
+++ b/infrastructure/nginx/Dockerfile
@@ -0,0 +1,9 @@
+FROM nginx:alpine
+
+COPY nginx.conf /etc/nginx/nginx.conf
+
+# Create directories for HLS and frontend
+RUN mkdir -p /usr/share/nginx/html/hls /usr/share/nginx/html/admin
+
+EXPOSE 80
+CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
diff --git a/infrastructure/nginx/nginx.conf b/infrastructure/nginx/nginx.conf
new file mode 100644
index 0000000..24b81b0
--- /dev/null
+++ b/infrastructure/nginx/nginx.conf
@@ -0,0 +1,40 @@
+events {
+ worker_connections 1024;
+}
+
+http {
+ server {
+ listen 80;
+ server_name localhost;
+
+ root /usr/share/nginx/html;
+ index index.html;
+
+ # Serve static frontend
+ location / {
+ try_files $uri $uri/ /index.html;
+ }
+
+ # Proxy chat WebSocket
+ location /ws {
+ proxy_pass http://chat:3000;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_set_header Host $host;
+ }
+
+ # Serve HLS from playout (with cache headers for Cloudflare)
+ location /hls/ {
+ alias /usr/share/nginx/html/hls/;
+ add_header Cache-Control "public, max-age=3600";
+ add_header Access-Control-Allow-Origin "*";
+ }
+
+ # Health checks
+ location /health {
+ return 200 "healthy";
+ add_header Content-Type text/plain;
+ }
+ }
+}
\ No newline at end of file