Distribution
The distribution domain manages CDN infrastructure nodes — Castis Streamer nodes, cproxy edge caches, and the colorbars UDP broadcaster. It also tracks the topology between them.
API base: /api/distribution/
Pages: /streamers, /streamers/:id, /streamers/new, /streamers/:id/edit, /caches, /caches/:id, /caches/new, /caches/:id/edit
Concepts
Streamer — a Castis Streamer node that ingests UDP/file sources and serves HLS/DASH
Cache — a cproxy edge cache node that sits in front of streamers
Origin key — a named routing slot on cproxy (e.g. food, ads) with a URL pattern
host — internal Docker DNS name used for CoreAPI → node communication (e.g. streamer_bkk)
publicHost — browser-accessible address used to build playback URLs (e.g. localhost)
Sync — pushing DB state to cproxy runtime config via PUT /api/config/origins
Node topology
Channel → ChannelOrigin → Streamer → CacheOriginStreamer → CacheOriginKey → Cache (cproxy)
Testing Distribution
Streamers — /streamers
Navigate to http://localhost:15173/streamers.
- Table lists all registered streamer nodes with host, ports, GPU flag, and status
- Ping — calls
GET /api/pingon the node and updates status toonline/offline - Edit — opens edit form
- Click row → streamer detail page
Streamer list with ping button and status indicators
Streamer detail — /streamers/:id
Shows: - Node info (host, publicHost, ports, GPU, last pinged) - Channels — all channels whose origins include this streamer - Cache assignments — all cproxy origin keys this streamer feeds into
Streamer detail showing hosted channels and cache assignments
Register a new streamer — /streamers/new
Fields:
- Name — unique display name (e.g. streamer-bkk)
- Host — Docker internal service name (e.g. streamer_bkk) — used for CoreAPI → Streamer API calls
- Public host — browser-accessible hostname (e.g. localhost) — used to build playback URLs
- HTTP port — streaming port (host-mapped for local nodes, e.g. 18080)
- API port — management API port (e.g. 18081)
- GPU — toggle if node has NVIDIA GPU for NVENC transcoding
host vs publicHost
For local Docker compose nodes: host = streamer_bkk, publicHost = localhost.
For staging on-prem nodes: host == publicHost = stg-onprem-main.cdn.3bbtv.com.
Caches — /caches
Navigate to http://localhost:15173/caches.
- Table lists all cproxy nodes with host, ports, number of origin keys, and status
- Ping — calls
GET /api/versionon cproxy and updates status - Click row → cache detail page
Cache list with origin key count
Cache detail — /caches/:id
Shows node info and the Origin Keys section for managing cproxy routing.
Cache detail with origin keys and sync button
Add an origin key
- Click + Add key
- Fill in:
- Key name — routing slot name (e.g.
food) - URL pattern — regex matching incoming paths (e.g.
^/food/.*$) - Rewrite match — strip prefix before forwarding (e.g.
^/food/(.*)$) - Rewrite replace — replacement (e.g.
/$1) - TTL — cache duration (e.g.
1s) - Priority — order in origins array (lower = matched first)
- Click Create key
Assign streamers to a key
Within each key row:
- Click + Add streamer
- Enter the streamer ID (find it at
/streamers) - Click Add
- First added = priority 0 (primary), second = priority 1 (failover)
To remove a streamer from a key, click ✕ next to the streamer name.
Sync to cproxy
After adding keys and assigning streamers, click ⚡ Sync to cproxy.
This calls POST /caches/:id/keys/sync which:
1. Reads all CacheOriginKey rows for this cache from DB
2. Builds the cproxy origins array JSON
3. Calls PUT /api/config/origins on cproxy
4. cproxy applies the config and writes it back to cproxy.yml on disk
The placeholder origin is replaced entirely. Config survives container restarts.
Full distribution test flow
# 1. Verify streamers are seeded
curl -s http://localhost:3000/api/v1/distribution/streamers | jq '.data[] | {id, name, status}'
# 2. Ping streamer-bkk (id=5)
curl -s -X POST http://localhost:3000/api/v1/distribution/streamers/5/ping | jq .
# 3. Check cproxy starts with placeholder
curl -s http://localhost:28081/api/config | jq '.origins[] | .key'
# → "placeholder"
# 4. Go to /caches/1 in UI
# → Add key "food" with pattern ^/food/.*$
# → Assign streamer-bkk (id=5) as primary
# → Assign streamer-ntb (id=6) as failover
# → Click ⚡ Sync to cproxy
# 5. Verify cproxy config updated
curl -s http://localhost:28081/api/config | jq '.origins[] | {key, urlPattern, originUrls}'
# 6. Test routing through cproxy
# (requires a stream or VOD file to exist on the streamer)
curl -I http://localhost:28080/food/kitchen.mp4
# 7. Check streamer topology
curl -s http://localhost:3000/api/v1/distribution/streamers/5/channels | jq .
curl -s http://localhost:3000/api/v1/distribution/streamers/5/caches | jq .
Colorbars — UDP broadcast simulator
The colorbars container at http://localhost:9999 runs ffmpeg subprocesses that broadcast SMPTE colorbars as MPEG-TS over UDP multicast. Use it to simulate a live UDP signal for channel creation and probe testing.
# create a colorbar stream
curl -X POST http://localhost:3000/api/v1/distribution/colorbars/streams \
-H "Content-Type: application/json" \
-d '{"id":"test1","multicastIp":"239.0.0.8","port":4444}'
# start broadcasting
curl -X POST http://localhost:3000/api/v1/distribution/colorbars/streams/test1/start
# probe from streamer-bkk — should detect signal
curl -X POST http://localhost:3000/api/v1/distribution/streamers/5/probe \
-H "Content-Type: application/json" \
-d '{"udp":"239.0.0.8","port":4444}'
# stop
curl -X POST http://localhost:3000/api/v1/distribution/colorbars/streams/test1/stop
Port constraint
Each colorbar stream must use a unique port. Two streams sharing a port — even on different multicast IPs — will interfere because Castis Streamer binds on port when joining a multicast group.
Key API Endpoints
GET/POST /api/distribution/streamers
GET/PUT /api/distribution/streamers/:id
POST /api/distribution/streamers/:id/ping
POST /api/distribution/streamers/:id/probe
GET /api/distribution/streamers/:id/channels
GET /api/distribution/streamers/:id/caches
GET/POST /api/distribution/caches
GET/PUT /api/distribution/caches/:id
POST /api/distribution/caches/:id/ping
GET/POST /api/distribution/caches/:id/keys
PUT/DELETE /api/distribution/caches/:id/keys/:keyId
POST /api/distribution/caches/:id/keys/:keyId/streamers
DELETE /api/distribution/caches/:id/keys/:keyId/streamers/:streamerId
POST /api/distribution/caches/:id/keys/sync