- Created README.md in docs folder outlining project structure and documentation links. - Added SECURITY.md detailing the security model, threat model, authentication, transport security, network isolation, and incident response procedures. - Introduced SETUP.md providing a detailed setup guide for local and production environments, including prerequisites, environment setup, and Gitea integration. - Added STREAMING.md outlining the streaming pipeline, including production flow, source-media contract, playout and HLS packaging, cache contract, player behavior, and monitoring. - Updated architecture documentation to include links to new SETUP.md and STREAMING.md files for better navigation.
7.0 KiB
API Reference
This document describes the currently implemented API surfaces in Linear Void.
Base URLs
- Public app and admin: https://linearvoid.tv
- WebSocket endpoint: wss://linearvoid.tv/ws
- Local fallback: http://localhost and ws://localhost/ws
Authentication Model
Admin REST endpoints use a cookie session.
- Cookie name: linearvoid_admin_session
- Cookie attributes: HttpOnly, SameSite=Strict, Secure when request is HTTPS (or X-Forwarded-Proto=https)
- Session TTL: controlled by ADMIN_SESSION_TTL_HOURS (default 12h)
Chat WebSocket API
Connect
Endpoint:
- GET /ws?username={name}&room={room}
Client sends JSON messages over the socket.
Message Envelope (client -> server)
{
"room": "main",
"username": "viewer123",
"text": "hello world",
"type": "public"
}
Notes:
- type is usually public for room chat and private for DM room routing.
- Private room names are formatted like dm:userA-userB (sorted usernames).
- Moderation/system events are sent as type=system messages.
Message Envelope (server -> client)
{
"room": "main",
"username": "viewer123",
"text": "hello world",
"type": "public",
"timestamp": "2026-08-04T12:34:56Z"
}
Admin Session API
Login
- POST /api/admin/login
- Auth: none
Request:
{
"username": "admin",
"password": "your-password"
}
Success response:
{
"authenticated": true,
"username": "admin"
}
Logout
- POST /api/admin/logout
- Auth: optional (clears cookie either way)
Response:
{
"authenticated": false
}
Session Check
- GET /api/admin/session
- Auth: cookie required
Success response:
{
"authenticated": true,
"username": "admin"
}
Failure:
- 401 unauthorized
Admin Data API
All endpoints below require admin authentication.
Overview
- GET /api/admin/overview
Returns aggregate counters and runtime/system snapshot fields.
Playlist
- GET /api/admin/playlist
- PUT /api/admin/playlist
PUT request:
{
"content": "file '/app/media/video1.mp4'\nfile '/app/media/video2.mp4'\n"
}
Schedule
- GET /api/admin/schedule
Returns parsed playlist entries plus matched media metadata/runtime.
Moderation
- GET /api/admin/moderation/logs?limit=50
- POST /api/admin/moderation/actions
Action request:
{
"action": "mute",
"target": "username-or-id",
"moderator": "admin",
"reason": "spam",
"room": "main"
}
Analytics
- GET /api/admin/analytics
Returns totals (messages, moderation actions, rooms) and top room summary.
System
- GET /api/admin/system
Returns service health snapshot for chat/playout/redis/database and config metadata.
Active Users
- GET /api/admin/users
Returns currently discovered presence users (room + username) when Redis is available.
Media Library API
Media Collection
- GET /api/admin/media
- POST /api/admin/media (multipart upload, legacy path)
Multipart form field name: file
Media Item
- GET /api/admin/media/{id}
- PUT /api/admin/media/{id}
- DELETE /api/admin/media/{id}
PUT request:
{
"status": "ready",
"title": "My Clip",
"tags": "music,live"
}
Resumable Upload API
Maximum file size: 2GB
Default chunk size: 8MB
Initialize or Resume Session
- POST /api/admin/media/uploads
Request:
{
"uploadId": "optional-existing-id",
"fileName": "clip.mp4",
"fileSize": 123456789,
"mimeType": "video/mp4",
"chunkSize": 8388608,
"totalChunks": 15
}
Response:
{
"uploadId": "hex-token",
"fileName": "clip.mp4",
"fileSize": 123456789,
"chunkSize": 8388608,
"totalChunks": 15,
"uploadedChunks": 3,
"progress": 20
}
Check Upload Status
- GET /api/admin/media/uploads/{uploadId}
Returns same status envelope as initialize.
Upload Chunk
- PUT /api/admin/media/uploads/{uploadId}/chunk?index={chunkIndex}
- Content-Type: application/octet-stream
Response returns updated upload status envelope.
Complete Upload
- POST /api/admin/media/uploads/{uploadId}/complete
On success returns admin media listing payload (files + generatedAt).
Metrics and Health
Chat metrics
- GET /metrics
Prometheus exposition includes connected clients, stored messages, moderation actions, Redis indicators, active admin sessions, and build info.
Health
- GET /health on chat service
- GET /health on playout service
- GET /health on nginx reverse proxy
HTTP Status Conventions
| Code | Meaning | When |
|---|---|---|
| 200 | OK | Successful GET, PUT, or action |
| 201 | Created | Successful POST (upload init, override created) |
| 204 | No Content | Successful DELETE, CORS preflight |
| 400 | Bad Request | Malformed JSON, missing required fields, invalid input |
| 401 | Unauthorized | Missing or expired admin session |
| 403 | Forbidden | Valid session but insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 405 | Method Not Allowed | Wrong HTTP method for endpoint |
| 409 | Conflict | Schedule override overlap, upload ID mismatch |
| 413 | Payload Too Large | Upload exceeds 2 GB limit |
| 422 | Unprocessable | File fails normalization check (codec, fps, resolution) |
| 429 | Too Many Requests | Rate limit exceeded (chat messages, WS connections, login) |
| 500 | Internal Server Error | Unexpected server failure |
| 503 | Service Unavailable | Redis unavailable for user actions, playout health degraded |
Rate Limiting
All limits are per-IP (from X-Forwarded-For or RemoteAddr) unless noted.
| Resource | Limit | Window | Response |
|---|---|---|---|
| Chat messages | CHAT_RATE_LIMIT_MAX (default 10) |
CHAT_RATE_LIMIT_WINDOW_S (default 10s) |
429 + {"type":"system","text":"Rate limit exceeded"} |
| WebSocket connections | 5 | 60s | 429 + "too many connections from this IP" |
| Admin login | 5 | 5min | 429 + "too many login attempts, try again later" |
Rate limit headers on applicable responses:
X-RateLimit-Limit: maximum requests per windowRetry-After: seconds until window resets- 413: payload too large
- 500: internal server error