Continuous relays
Quikcast can pull an operator-configured HTTP/HTTPS continuous encoded stream into a dedicated local mount. Relays use the existing source lease, codec/container handling, generation ring, listener limits, metadata state and listener fanout. They do not generate HLS, transcode, decode, remux, invoke subprocesses, replace an encoder source or provide fallback/failover policy.
Configuration and ownership
Configure the local mount normally, then associate exactly one [[relays]] definition with its full canonical path. The mount's local credential remains the source/metadata scope, but external encoder admission to a relay-owned mount always rejects after authentication, including while the relay is disabled or stopped. Remote authentication is independent and optional:
Secret file paths resolve relative to the configuration file. Upstream username and secret file must be supplied together. Usernames are bounded to 64 printable ASCII bytes without a colon; passwords to 1–512 printable ASCII bytes, with one final LF/CRLF stripped. A short password may be required by a remote server; this does not weaken the existing local machine-secret contract. Read errors and validation diagnostics do not reveal the password/path. URL userinfo, fragments, whitespace, invalid ports and non-HTTP(S) schemes reject startup. URLs are at most 2,048 bytes. No URL, upstream credential, raw upstream body or raw TLS/client error appears in native relay inspection or logs.
Relays are static operator-authorized egress configuration. There is no arbitrary-URL management endpoint. Remote private addresses are valid for intentional private origins. Self-targets matching the configured binding or actual resolved public/admin listening address reject. This local check cannot discover proxy-mediated or multi-server loops; operators own topology correctness. Redirects are not followed; configure the final endpoint. Configuration and credential changes require restart. enabled=false starts stopped; an operator can start it through the native API, but that desired state is volatile across restart.
At most 16 relays are configured, each mapped to a different existing continuous mount (within the existing 64-mount limit). [limits].relay_allocation_budget defaults to 33,554,432 bytes and must cover a fixed 2,097,152-byte reservation per configured relay, including disabled relays. This is conservative admission accounting for bounded worker/DNS/HTTP/TLS staging, separate from the existing encoded-media/retained-generation budget; it is not a total RSS or kernel-memory promise. One HTTP connection/attempt is active per relay, separately from public/admin inbound connection caps. DNS uses at most four system nameserver entries, serial server queries, one resolver attempt and no search suffix expansion/cache. The bounded /etc/hosts snapshot (128 KiB input ceiling) loads once at startup, outside worker I/O, and is shared across attempts. At most 16 addresses are tried within the single overall connect deadline. Each attempt owns a task scope capped at 16 async DNS/HTTP driver tasks, closed, aborted and joined before retry. No blocking OS resolver jobs or detached retry tasks are used. Fixed/shared TLS trust roots and allocator/OS overhead are not charged as media bytes.
Remote wire and metadata contract
Only HTTP/1 status 200 continuous audio is accepted. Supported MIME mappings are audio/mpeg, audio/aac, audio/aacp, audio/ogg and application/ogg. Existing MP3/AAC frame and Ogg page/header/codec validation still apply; the server does not validate decoded audio. HLS playlists, HTML, redirects and upstream error statuses reject locally. Legacy ICY 200 OK status syntax is not supported in this milestone. HTTP Content-Length, chunked and close-delimited bodies use Hyper framing. Duplicate critical headers, Content-Length plus Transfer-Encoding, non-chunked transfer encodings, compression, malformed/truncated framing and trailers reject the attempt. Requests ask for identity encoding. Response headers are limited to 16 KiB / 64 fields and transport reads to fixed 16 KiB buffers. The maintained client handles finite HTTP framing; the live body is never collected in memory.
Relays request icy-metadata: 1. MP3/AAC responses may omit metadata or advertise an integer icy-metaint from 1 to 16,777,216. The demultiplexer counts audio bytes after HTTP dechunking and removes all remote ICY blocks before source publication. A remote length byte declares at most 4,080 metadata bytes; staging is fixed to that maximum. This is distinct from the local 16,000-byte listener interval and smaller local title output. Ogg responses with icy-metaint reject; Ogg comments remain in-band and are neither extracted nor rewritten.
Station response headers use an allowlist: icy-name, icy-description, icy-genre, icy-url, icy-pub, icy-br. Duplicates, invalid optional values/control characters and metadata beyond an 8 KiB aggregate are omitted. Publication safely retains those values in the existing station metadata model. Unknown headers are not mirrored into listener responses.
Track parsing accepts unambiguous Key='value'; fields and a single StreamTitle. Unknown well-formed fields are ignored. Invalid UTF-8, malformed syntax/padding, duplicate titles, controls, semicolons in titles or titles beyond 1,024 bytes increment a rejection counter and preserve the previous title. A zero-length metadata block means unchanged; an explicit empty StreamTitle clears the title. Apostrophes/backslashes follow the existing local verbatim title contract; ambiguous separators are rejected. Locally negotiated ICY is serialized afresh from validated generation-local state. Truncated ICY framing terminates only that relay attempt because guessing audio boundaries would corrupt audio.
Retry, drain, stop and shutdown
States are stopped, connecting, connected, backoff. Connected means the validated source lease has started; it does not promise Ogg initialization/media readiness. Source inspection supplies codec/readiness context. desired_running records operator intent independently of the current worker state. Stop/reconnect controls return accepted state and complete asynchronously, with at most one attempt active; the old attempt and its tasks are released before a successor begins.
Connect timeout covers DNS, sequential address connection and TLS handshake together. Header timeout covers sending GET and receiving response headers. Audio timeout counts progress in encoded audio, not just HTTP/body or metadata bytes. Healthy continuous sessions have no total-duration limit. All configured delays/deadlines are 1–3,600,000 milliseconds, initial delay must not exceed max, and jitter is 0–50 percent.
Failures enter exponential backoff: initial delay, doubling, capped at max. Jitter varies the nominal delay within its configured percentage but never below initial or above max. Only a connection lasting stable_connection_ms resets the nominal backoff. DNS, EOF/reset, TLS verification, HTTP/authentication, timeout, unsupported format and media/framing failures are relay-local. Persistent failures retry at the bounded rate; certificate verification is never bypassed. Operators see fixed error categories, plus a numeric upstream status when available. No HTTP response body is logged.
An upstream failure closes that source generation and its listeners. Recovery starts a fresh generation with empty track state, and players must reconnect. Seamless listener migration, source priority and automatic multi-upstream failover are outside this milestone.
DELETE relay control or DELETE its native source resource suppresses retry until an explicit POST reconnect/start, or restart according to static enabled. A generation-guarded source disconnect atomically marks relay intent stopped while cancelling the selected generation, preventing retry from undoing the action. Stale source/revision controls fail with 409. A reconnect is a deliberate cancellation plus a new attempt, not source replacement.
Drain preserves an already connected/admitted generation. It prevents new attempts and reconnect commands; a connecting/backoff relay stops with reason server_draining, and a connected relay that later fails does not retry. Shutdown cancels DNS/connect/TLS/header/body/backoff waits, closes generation resources and joins supervised tasks within the existing server shutdown deadline. Existing accepted-admission linearization still applies to drain races. Stop remains available during drain.
Native API
All endpoints use the existing loopback/private management listener, dedicated Bearer authentication, no-body request validation, bounded native error envelope and Cache-Control: no-store:
GET /api/relays: ordered array of all configured relay snapshots.GET /api/relays{mount}: relay snapshot using the full mount path, for example/api/relays/remote.mp3or/api/relays/station/live.mp3.DELETE /api/relays{mount}?revision=N: request stop; optional revision guard. Returns 202 and a snapshot.POST /api/relays{mount}?revision=N: request reconnect/start; optional revision guard. Returns 202 and a snapshot, or 503 while draining/shutting down.
Only GET is accepted on the collection; detail allows GET/POST/DELETE. Unknown relay returns 404 relay_not_found, invalid parameters 400 invalid_request, stale revision 409 conflict, wrong method 405, unauthorized 401. revision is the control revision, not the source generation; it increments on each accepted operator command, including native source-disconnect. Numeric identities/counters are ephemeral across restarts. Query parameters are forbidden for GET. Source generation guards remain available through DELETE /api/sources{mount}?generation=N.
Snapshots expose mount, current state/desired intent, control revision, attempt sequence/current source generation, connection start/monotonic age, next retry wall-clock timestamp/monotonic remaining delay and scheduled delay, stop reason, sanitized last error/status, attempts/connections/reconnects, de-framed response-body bytes, published encoded-audio bytes, metadata rejections and the configured allocation reservation. response_body_bytes_total excludes HTTP chunk framing and TLS/socket overhead; it includes remote ICY. Audio bytes exclude ICY. These are publication/receipt observations, not proof that a player consumed media. Stop responses may still show the prior connection while cleanup completes; poll state=stopped to confirm completion.
/api/stats.relays provides aggregate states, counters and reservations. /api/server.capabilities includes continuous-relays when configured. Prometheus adds fixed unlabeled relay_attempts_total, relay_connections_total, relay_reconnects_total, relay_failures_total, relay_response_body_bytes_total, relay_audio_bytes_total and relay_metadata_rejections_total. Existing source/listener metrics also account for the shared domain. No dynamic upstream/mount/client labels are introduced.
Operator runbook
Build with the locked dependencies, copy the example configuration (config--quikcast.example.toml), create distinct local source/HLS/management secret files, and supply the upstream password only when remote Basic authentication requires it. Validate/start with
quikcast --config PATH; all configuration/secret setup fails before serving.Keep public audio/HLS delivery separate from the loopback administrative binding. External TLS termination remains accepted for inbound traffic; outbound HTTPS uses the bundled WebPKI roots and hostname/IP certificate verification. Custom CA roots/client certificates are not configuration options in this milestone. The test-only TLS fixture never enters the production trust store.
Provide a draining nonregular stdout collector on Unix. Disable proxy upload/response buffering, caching and short streaming timeouts; ensure PUT/SOURCE interoperability. Only trusted immediate peers influence XFF inspection. Do not expose private API/HLS producer routes through a public unrestricted proxy.
Poll private health/readiness,
/api/relays,/api/mounts,/api/sources{mount}and/api/stats. A disconnected upstream does not make global readiness fail. Connect continuous players to the local mount; relay failure ends that generation, so choose players with reconnection behavior appropriate to this contract.Stop/reconnect through
/api/relays{mount}with the latest control revision; disconnect a source with its current generation if desired. Inspect stop reason/next retry before intervening. Normal encoder and source-authenticated metadata workflows remain documented in management-api.md and the earlier milestone/protocol notes.Keep HLS producers external: explicitly ingest/publish MPEG-TS assets through the existing typed API. HLS is never derived from a relay. Whole-object player compatibility/Range limitations remain unchanged.
Before service shutdown, invoke drain and remove readiness from routing. Existing relay/continuous sessions continue until source failure or shutdown; HLS reads continue but producer mutations are blocked. Then SIGINT/SIGTERM invokes bounded cancellation/joining. Drain is one-way and does not automatically shut down or resume.
Apply config/secret changes by restart. Sources/relays reconnect; continuous generations/listeners/history/counters are process-scoped. External HLS producers must repopulate volatile HLS assets. Static enabled relays start again after restart; an administrative stop is not persisted. Current public/admin inbound capacity is shared and does not reserve operator access under saturation; size OS/proxy/descriptor headroom separately.
Feature acceptance and remaining gates
Automated correctness coverage includes chunked/exact-byte audio, independently negotiated ICY, malformed metadata/text/framing, four formats and active Ogg late joins, EOF retry, stop/source-disconnect/reconnect guards, incomplete headers, drain, inactive-audio metadata traffic, unrelated continuous/HLS/API operations, certificate verification with a test-only trust root, and cancelled DNS driver cleanup. The relay milestone review records the executed results and standalone origin-to-edge curl smoke evidence.
This implementation does not declare feature freeze or complete comprehensive manual encoder/player/proxy validation, optimization/hardening or production qualification. The approved roadmap remains the sequencing authority. Optional/deferred source policy, deployment/media expansion and platform integration remain excluded.
PERFORMANCE OPTIMIZATION REMAINS BLOCKED UNTIL FEATURE FREEZE.