Milestone 2: MP3 vertical slice
The server implements the reviewed continuous MP3 contract. Encoded bytes remain opaque. This slice introduces no ICY interleaving, track-update endpoint, codecs, HLS registry, or external media processes. The original reference evidence remains the source for compatibility decisions; integration tests exercise Quikcast separately.
Running and configuration
Use cargo run --locked -- --config PATH with the example configuration (config--quikcast.example.toml). Create its referenced secret file yourself using a high-entropy machine credential of 16–512 bytes. One final newline is permitted. The default username is source; credential files resolve relative to the TOML file. Typed configuration rejects unknown fields, invalid bounds, insufficient reservation budgets, ambiguous secrets, noncanonical/duplicate mounts, and public administrative bindings before listening. Configuration files are limited to 128 KiB. QUIKCAST_CONFIG is an alternative to the CLI path; QUIKCAST_LOG overrides the typed logging filter.
The public socket serves configured mounts. A separate loopback socket exposes /health/live, /health/ready, and /metrics; the public socket does not expose these endpoints. SIGINT/SIGTERM cancels the owner, closes acceptors, cancels generations, drains supervised connections, and aborts then joins any tasks exceeding the shutdown deadline. A library caller must cancel BoundServer::shutdown_token() and await run(); dropping that future is cancellation by its caller, not graceful draining.
TLS termination and public routing belong to the deployment proxy. Basic machine credentials are padded and compared using constant-time primitives, without a hash-storage scheme. Credentials and authorization headers are never logged.
Protocol implemented
EOF-delimited PUT and legacy
SOURCEusing HTTP/1.0, HTTP/1.1, orSOURCE … ICE/1.0receive early HTTP/1.0 200 after successful admission. RawExpect: 100-continuereceives 100 followed by early 200. This is the chosen legacy admission contract, supported by the 2.4.4 reference observations; it is not a claim that all Icecast releases reply this way.PUT with Content-Length or chunked framing uses Hyper's HTTP body decoder. Authentication and mount admission precede polling the body and sending 100. Audio reaches listeners before upload completion; final 200 follows valid completion. Malformed framing returns 400; inactivity returns 408. The source body has no cumulative duration/byte ceiling because it is a live stream, but every resident input block is bounded.
Only
audio/mpegis accepted. Station metadata headers are normalized into selected ICY response headers. GET returns an HTTP/1.0 close-delimited audio stream, without chunk markers. HEAD reports headers without listener admission. Requesting ICY metadata still yields plain audio withouticy-metaintat this milestone.Header limits are 16 KiB/64 fields, path targets 2 KiB, selected source metadata 8 KiB, and decoded Basic credentials 1 KiB. Ambiguous duplicate framing/authentication headers, duplicate Host, missing HTTP/1.1 Host, encoded separators, dot segments, and unsupported expectations are rejected. Connections use one request and then close. Requests to listeners/admin routes cannot carry a nonempty body.
Duplicate source claims return 409; listener/generation capacity returns 503. No source queues behind listeners. Incomplete headers time out. Closing a rejected request with unread bytes may reset TCP; clients must tolerate this normal bounded rejection behavior.
Ownership and allocation bounds
server owns two acceptors and one JoinSet. Each admitted connection acquires connection and handshake permits before spawning; completed tasks are reaped before new accepts. No per-chunk task, listener channel, registry task, or metrics task exists. The caller owns the server future. Registry mounts are static and immutable after startup. Short admission/ring/notification locks never span await or socket I/O.
Each source lease claims a monotonically checked generation identity, first Connecting then Streaming. Drop closes only its own generation and clears the active slot only if its identity still matches. Generation permits remain owned by the final generation reference. Two retained generations per mount are reserved, including an old generation still held by a closing listener. Concurrent claims have one winner. Disconnects and cancellation release listener permits and tracked references through ownership guards.
A ring has independent byte and entry bounds, defaulting to 4 MiB and 4,096 entries. Source reads/copies are at most 16 KiB. Ring entries own exact-length payload vectors wrapped in Bytes owners. Listener clones and partial late-join slices retain that entire allocation; the owner updates the allocation gauge only on its last drop. Payload bytes are copied once into the ring; listener application delivery shares those bytes, followed by OS socket copying. Hyper-framed input and prefix replay introduce separately bounded input buffers. There is no per-listener copy of the retained ring.
A listener may emit one frame until the underlying transport flush completes. This bounds both bytes and tiny-chunk reference counts. A connection-owned 50 ms monitor detects a cursor older than the ring floor even if Hyper stops polling the body; pending writes have their own deadline. Disconnecting that listener releases its owners without blocking source reads. The monitor's CPU cost at large connection counts remains a milestone 3 measurement.
Startup reserves:
2 × mounts × (ring_bytes + ring_entries × (2 × sizeof(Entry) + 128) + 16 KiB + 8 KiB)
+ max_listeners × 32 KiB ≤ retained_audio_budget.
Two entry widths allow backing-vector rounding; ring construction rejects larger capacity. The extra 128 bytes per audio entry covers the pinned Bytes owner/refcount and allocation bookkeeping. Listener reservation allows two bounded frame allocations, including slices retaining full backing data. Input and station-header allowances are charged per retained generation. All arithmetic is checked before binding. This reservation is conservative application accounting, not an RSS promise: separately include connection futures, bounded parser/Hyper buffers, registry/notification structures, logging, allocator arenas, kernel socket buffers, and file descriptors. retained_audio_allocation_bytes measures payload backing bytes through final-reference drop; it does not include allocator overhead or all process memory. Changing the Bytes/allocator layout requires reviewing the bookkeeping allowance.
The default retained-audio budget is 1 GiB, checked against configured mount and listener maxima. It reserves headroom before admission; ingest never waits for evicted listener references to free a global payload permit. Temporary joins begin up to 64 KiB behind the live cursor at an arbitrary byte offset. This is deliberately temporary pending minimal MPEG frame awareness.
Observability and dependency choices
Fixed atomic Prometheus counters/gauges track sources/mounts/listeners, accepted connections, actual listener payload bytes accepted by the socket, source bytes, live backing allocations, errors, admission refusal, disconnect reasons, slow consumers, readiness, and bounded authentication-duration histogram buckets. No user-controlled metric labels or process-global recorder are needed. Structured logs include source/listener identity, mount, duration, disconnect reason, and admission context. The milestone 2 tracing formatter wrote synchronously to standard output. Milestone 3 demonstrated that a stopped collector could halt serving, and replaced it with bounded nonblocking record writes and a loss counter; see the proof and operational contract.
Cargo.lock pins the tested graph; validation used Rust/Cargo 1.99.0. Tokio owns I/O/scheduling/semaphores/watch; Hyper supplies standard framing; httparse supplies the bounded admission grammar shared by raw/standard paths; Bytes supplies immutable shared payload ownership. HTTP/body utilities, serde/TOML, thiserror, tracing, base64, and subtle have concrete uses. Axum, metrics recorder/exporter libraries, DashMap, storage traits, codec factories, unsafe code, and lock-free structures are absent. There are no custom password hashes.
Validation and review boundary
Unit tests cover credential lengths/scope, canonical paths/framing, metadata normalization, invalid configuration and overflow, stale-generation cleanup, simultaneous source admission, allocation lifetime, tiny-chunk entry limits, model-based varied ring eviction/joins, and sequence exhaustion.
Loopback integration tests use the captured MP3 payload and verify byte-exact multi-listener delivery, raw/legacy admission, Expect timing, length/chunked decoding before completion, over-read initial payloads, disconnect/reconnect, private metrics, failed authentication, hostile framing, mount isolation, unsupported media, HEAD, oversized/slow headers, listener-capacity release, idle listener disconnect, malformed chunked upload isolation, source inactivity, shutdown allocation release, and an actual stopped socket alongside a healthy reader. The stalled-reader case sends 8 MiB through a 512 KiB ring and checks source progress, healthy delivery, eviction, and retained allocations. It is a correctness test, not a throughput benchmark.
Required checks: cargo fmt --all --check, cargo clippy --all-targets --all-features -- -D warnings, cargo test --all, and cargo check --all-targets. On the development sandbox, socket tests require permission to bind loopback. Cargo audit is unavailable on this host and is reported rather than claimed as passed. cargo build --locked followed by python3 tools/cli_smoke.py exercises the binary using disposable secret files, curl, ephemeral loopback bindings, and SIGTERM; all subprocesses have bounded waits and cleanup.
Milestone 3 remains: sustained Linux RSS/allocation measurements, real-client compatibility against Quikcast, reconnect/reset churn at scale, adversarial/property/fuzz coverage, monitor/log-sink cost, and realistic throughput/latency benchmarks. No production capacity or deployment-readiness claim follows from these socket tests. Proceed through that proof before ICY, AAC/Ogg, or MPEG-TS HLS.
Validation recorded on 2026-10-07: 11 unit tests and 8 loopback integration tests passed; formatting, Clippy with warnings denied, all-target compilation, locked build, and the curl/SIGTERM CLI smoke test passed. Cargo audit was unavailable. No benchmark or large-scale RSS result is asserted.