Unified REST API for all SpaceMusic services.
Overview¶
The API gateway at api.spacemusic.tv is a custom SvelteKit application that provides a single REST interface to all SpaceMusic backend services. It proxies requests to LiveKit, MinIO, Centrifugo, Authentik, Grafana, and Kuvasz, handling authentication, rate limiting, and CORS.
Stack: SvelteKit 2.x (adapter-node), shadcn-svelte, Tailwind CSS v4
Deployment: Docker Compose + GitHub Actions SSH (migrated from devpush to standalone Docker for stable container hostname and internal service communication)
Authentication¶
The API supports three authentication methods:
- SSO headers -- When accessing via browser, Traefik forwards
X-authentik-usernameheaders from Authentik. Used by the dashboard UI. - API key --
X-API-Keyheader or?api_key=query parameter. Manage keys atapi.spacemusic.tv/keys. - Bearer token --
Authorization: Bearer <token>validated against Authentik's user API.
Public endpoints (no auth required): GET /api/health, GET /api/openapi.json, GET /api/monitor/status
Endpoints¶
52 endpoints across 8 service groups:
| Service | Base Path | Endpoints | Technology |
|---|---|---|---|
| Stream | /api/stream/* |
12 | LiveKit |
| Storage | /api/storage/* |
7 | MinIO |
| Relay | /api/relay/* |
8 | Centrifugo |
| Auth | /api/auth/* |
10 | Authentik |
| Dashboards | /api/dashboard/* |
6 | Grafana |
| Monitor | /api/monitor/* |
7 | Kuvasz |
| Connect | /api/connect/* |
-- | Node-RED (stub, 501) |
| Data | /api/data/* |
-- | SpacetimeDB (stub, 501) |
Stream Endpoints¶
| Method | Path | Purpose |
|---|---|---|
| GET | /api/stream/rooms |
List active rooms |
| POST | /api/stream/rooms |
Create room |
| GET | /api/stream/rooms/{name} |
Room detail + participants |
| DELETE | /api/stream/rooms/{name} |
Delete room |
| GET | /api/stream/rooms/{name}/participants |
List participants |
| POST | /api/stream/tokens |
Generate LiveKit JWT |
| GET | /api/stream/ingress |
List ingress streams |
| POST | /api/stream/ingress |
Create ingress (RTMP/WHIP/URL) |
| DELETE | /api/stream/ingress |
Delete ingress |
| GET | /api/stream/egress |
List egress (recordings) |
| POST | /api/stream/egress |
Start recording/restream |
| DELETE | /api/stream/egress |
Stop egress |
Storage Endpoints¶
| Method | Path | Purpose |
|---|---|---|
| GET | /api/storage/buckets |
List all buckets |
| GET | /api/storage/buckets/{bucket} |
Check bucket exists |
| GET | /api/storage/buckets/{bucket}/objects |
List objects |
| GET | /api/storage/buckets/{bucket}/objects/{key} |
Download object |
| PUT | /api/storage/buckets/{bucket}/objects/{key} |
Upload object |
| DELETE | /api/storage/buckets/{bucket}/objects/{key} |
Delete object |
| POST | /api/storage/presigned |
Generate presigned URL |
Relay Endpoints¶
| Method | Path | Purpose |
|---|---|---|
| POST | /api/relay/publish |
Publish to a channel |
| POST | /api/relay/broadcast |
Publish to multiple channels |
| GET | /api/relay/channels |
List active channels |
| GET | /api/relay/channels/{channel}/presence |
Connected clients |
| GET | /api/relay/channels/{channel}/history |
Message history |
| DELETE | /api/relay/channels/{channel}/history |
Clear history |
| POST | /api/relay/tokens |
Generate JWT token |
| GET | /api/relay/info |
Server node info |
Auth Endpoints¶
| Method | Path | Purpose |
|---|---|---|
| GET | /api/auth/me |
Current user profile |
| GET | /api/auth/users |
List users |
| POST | /api/auth/users |
Create user |
| GET | /api/auth/users/{id} |
Get user |
| PATCH | /api/auth/users/{id} |
Update user |
| DELETE | /api/auth/users/{id} |
Delete user |
| GET | /api/auth/groups |
List groups |
| POST | /api/auth/groups |
Create group |
| GET | /api/auth/tokens |
List API tokens |
| POST | /api/auth/tokens |
Create API token |
Dashboard Endpoints¶
| Method | Path | Purpose |
|---|---|---|
| GET | /api/dashboard/dashboards |
Search dashboards |
| GET | /api/dashboard/dashboards/{uid} |
Get dashboard JSON |
| GET | /api/dashboard/alerts |
List alert rules |
| GET | /api/dashboard/annotations |
List annotations |
| POST | /api/dashboard/annotations |
Create annotation |
| POST | /api/dashboard/query |
Proxy datasource query |
Monitor Endpoints¶
| Method | Path | Purpose |
|---|---|---|
| GET | /api/monitor/monitors |
List HTTP monitors |
| POST | /api/monitor/monitors |
Create monitor |
| GET | /api/monitor/monitors/{id} |
Get monitor + stats |
| PATCH | /api/monitor/monitors/{id} |
Update monitor |
| DELETE | /api/monitor/monitors/{id} |
Delete monitor |
| GET | /api/monitor/heartbeats/{id} |
Uptime event history |
| GET | /api/monitor/status |
Public status page data |
Response Format¶
All endpoints use a consistent envelope:
// Success
{ "data": { ... }, "error": null }
// Error
{ "data": null, "error": { "code": "ROOM_NOT_FOUND", "message": "...", "status": 404 } }
Interactive Docs¶
Full interactive API documentation is available at api.spacemusic.tv/docs with a Stripe-style two-column layout. Each endpoint includes parameter tables, curl examples, and a "Try It" panel for live request execution.
The OpenAPI 3.1 specification is available at api.spacemusic.tv/api/openapi.json.
Rate Limiting¶
In-memory sliding window: 100 requests per 60 seconds per identity. Returns 429 Too Many Requests with Retry-After header. Public endpoints are exempt.
CORS¶
Allows requests from *.spacemusic.tv and localhost (any port). OPTIONS returns 204 with a 24-hour Access-Control-Max-Age.
Monitoring¶
Structured JSON request logs go to stdout and are scraped by Loki. The "API Gateway" Grafana dashboard (uid: api-gateway) shows health status, container metrics, and request logs.