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