av-mesh is the local prototype for a demand-driven audio/video mesh. A
contributor can publish media into one region. The service stores all media in
playlist and cache streams. Cache slots replicate to other regions through the
shared Wavey RaptorQ-FEC datagram protocol. Users can read replicated LL-HLS
artifacts or other stream-addressed slots from any region.
RaptorQ-FEC is the mesh transport. Cache synchronization inside the mesh moves
opaque stream bytes over the Wavey RaptorQ datagram protocol.
Contributor-facing RIST, SRT, and RTMP input belongs in ../av-contrib. That
service terminates the protocols and packages sender-agnostic media as fMP4/CMAF
LL-HLS artifacts. It publishes only stream-addressed artifact bytes into the
mesh FEC socket.
Transport reliability stance: RaptorQ-FEC is the right live mesh hot path for bounded packet loss because it can repair without waiting for RTT. It is not a general replacement for RIST/SRT ARQ on arbitrary bad WAN paths. If loss exceeds the configured repair budget, FEC fails closed. RIST/SRT can still recover later by retransmitting from history if the latency budget allows. The mesh should therefore stay slot and artifact native for live RaptorQ propagation. It needs an explicit missing-slot repair and backfill path before it can provide RIST-class eventual reliability.
This path can use TCP, QUIC, or a RIST-like ARQ channel.
The first implementation keeps the mesh transport intentionally small:
-
playlists::meshprovides UDP-FEC cache discovery and slot replication using the reusableraptorq-datagram-feccrate. Seed peers and private-subnet broadcast discovery only bootstrap the peer set. Normal meshHELLOframes gossip known peer addresses after that. -
Cache nodes use
--cache-mesh-role edge|distributor|peer. An edge is a leaf. It receives from same-region distributors and never forwards cache data. Production cache nodes enable same-region validation for the mesh protocol. Use--cache-mesh-regionwhen the cache region differs from telemetry region.--cache-mesh-max-parentsdefaults to 2 and bounds repair requests. A distributor sends objects to at most 8 children by default with--cache-mesh-max-children. -
Optional
private-subnet-discoverysupport can add mesh peers discovered on a 10.x private subnet by the existingdiscoverycrate's VLAN broadcast path.linode-private-discoveryis an alias for the same generic feature. Use this alias for Linode VLAN deployments. Thelinodecrate provides the required API. -
Playlist reads and warm-stream controls send mesh replica requests, so a node with local demand can ask peers for a stream immediately.
-
Replica requests begin at the earliest missing retained slot for the requested stream. This behavior gives live playlist, tail, and media requests a retained-window backfill path. It does not change the FEC datagram wire format.
-
LL-HLS playlist rendering is cached by stream and
ChunkCachecontent version. Slot writes and stream reuse invalidate the derived manifest. Unchanged requests do not repeatedly take each retained-slot read lock. -
web-serviceserves the operator/UI edge over HTTPS/TCP: HLS playlists, parts, segments, health checks, JSON mesh snapshots, server-sent mesh events, and HTTPPOST /api/control/*commands. This is not the mesh transport. -
Mesh control commands are sent node-to-node over TCP changes as
AVMCframes. A local HTTP/API command records the intent. It can apply the command locally. It publishes anAVMCenvelope to telemetry peers for region or node targeting. -
Optional raw TCP accepts the same snapshot/control and serialized media access-unit bytes in length-prefixed frames. Completed access units are cached as stream slots.
-
HTTP/3, WebSocket, and WebTransport remain explicit edge options. Use
--edge-http3for HLS over HTTP/3. Use--edge-websocketor--edge-webtransportfor those protocols. -
message-packetizerremains available for bounded UDP-style announcements operator commands useAVMCover TCP changes. -
Contributor ingest through
../av-contribsupports arbitrary byte streams viaPOST/PUT /ingest?stream_id=..., pure-Rust RIST MPEG-TS, SRT MPEG-TS, RTMP/FLV, and HTTPPOST/PUT /media/access-unitnon-TS media access units. The service boxes protocol inputs into fMP4/CMAF before they enter the mesh. Raw RIST, SRT, RTMP, and MPEG-TS payloads stay outside the mesh boundary. -
The sibling
../av-contribproject is the contributor-facing repo boundary. It owns non-TS media access-unit query parsing. It usesraptorq-datagram-fecto decode serialized access units. These edge formats are no longer defined inside the mesh binary. -
/meshserves the Needletail operations dashboard supplied by the product supervisor. The dashboard presents service and feed health, compiled delivery programs, and independent source and repair lanes. It also presents RaptorQ recovery, deadline health, publication continuity, and realtime latency. -
/<stream-id>serves the Needletail player bundle supplied throughNEEDLETAIL_PLAYER_DIST. Player assets and LL-HLS media remain same-origin. The player selects native HLS when available. It also includes HLS.js for conformance and fallback tests. -
Optional
--telemetry-bindpublishes JSON mesh snapshots to a TCP changes feed with theAVMTtag for central aggregation without scraping stdout. Snapshots include the node's mesh socket address so/api/meshcan resolve peer addresses back to node ids for the topology graph when both sides report. The service removes remote snapshots older than--telemetry-stale-ms. These snapshots do not affect capacity, topology, replica plans, or regional controls. -
Optional
--telemetry-peerconnects a central node to other nodes' TCP changes feeds and merges theirAVMTsnapshots into/api/meshand/mesh. The same feed also carriesAVMCcontrol commands. Region-scoped commands include a target node ID when telemetry identifies one. Subscribers then run matching warm or close requests on specific nodes instead of using only region filters. -
Optional
--telemetry-fec-targetsends the node snapshot to a central--telemetry-fec-bindUDP collector through the sharedraptorq-datagram-feccodec. The collector inserts decoded snapshots into the same bounded aggregator used by/api/mesh. Browsers never connect to nodes individually. This lane is intended for controlled private networking. Keep the TLS/TCP changes path for authenticated control commands. -
FEC telemetry defaults to one snapshot every 5 seconds and a total 32 Kbit/s send budget. Payloads use named MessagePack inside a versioned 32 KiB envelope with a CRC, boot ID, and monotonic sequence. The sender retains at most two snapshots or 64 KiB. Under load, it replaces the oldest snapshot and sends source symbols before repair symbols. It skips remaining repair symbols when a newer snapshot waits. It does not emit per-datagram logs or write telemetry to a database.
-
/api/meshand/metricsexpose bounded FEC queue, send, receive, decode, duplicate, and error counters underorchestration.telemetry_fecandav_mesh_telemetry_fec_*. -
Optional
--provision-commandlets the UI/APIprovision_nodecontrol hand off to an operator-provided shell command. The command receivesAV_MESH_PROVISION_NODE_ID,AV_MESH_PROVISION_REGION,AV_MESH_LOCAL_NODE_ID,AV_MESH_LOCAL_REGION, andAV_MESH_CONTROL_ID.AV_MESH_PROVISION_ACTIONisprovisionorclose. -
--edge-lifecycleenables demand-based edge provisioning and idle edge draining. The planner samples with the replication interval, defaults to five seconds, keeps one edge per region, and limits a region to four edges. It provisions after sustained reader or egress pressure and closes an idle edge after fifteen minutes. -
With
--features linode-provisioner,--linode-provisionuses thelinodecrate to create a node. It attaches the node to the region VLAN with a10.0.0.x/24private address, reboot it, and update DNS. Use--linode-region-map mesh-region=linode-regionfor mesh names such asuk=gb-lon.linode-private-discoveryenables this provisioner together with private-subnet discovery. -
av_mesh::replicationcontains the tested replica planner for baseline continent/region staging, demand-triggered local replicas, node storage capacity, and anti-affinity for nearby nodes. Telemetry includes all active cache stream IDs. Baseline staging can pull non-default media and access-unit streams and the default playlist stream./api/meshexposes planned replicas for these stream IDs to the control API. -
/api/meshexposes stream ids as browser-safe decimal strings instream_id_textfields wherever a numericstream_idappears, including the local stream stats, active stream telemetry, planned replicas, and recent control commands. HTTP control requests acceptstream_idas either a JSON number or a decimal string. Browser clients should send strings for Snowflake ids. -
/api/meshalso exposesedge_serviceswith each node's advertisedplayback_base_url, active readers, served requests and bytes, and LL-HLS tail polls. It includes cumulative LL-HLS handler latency histograms and p95 latency. Operators can compare origin freshness with edge response time. Player-facing services such asav-llhlsshould use seed nodes only to discover candidates. They should score candidates locally and avoid routing all playback through a central service. -
/api/meshexposes the active cache-mesh transport policy: replication scan interval, FEC symbol size, minimum repair symbols, proportional repair ratio, and maximum repair symbols. The default policy has a one-symbol floor and 3% proportional repair. It avoids a fixed high redundancy cost for small parts. It also protects larger parts from loss of multiple packets. -
Playback requests can supply a CMCD
sidvalue. An overloaded edge continues admitted sessions and rejects new sessions with HTTP 429. The response identifies healthy same-region edges withLinkandX-Needletail-Alternate-Edges. The master playlist also contains duplicate variants for healthy DAG edges. -
/api/meshalso exposesmesh_fecruntime outcomes. These outcomes include source and repair datagrams, protected and wire bytes, and decoded objects. They also include repair recovery, late sources, incomplete or expired objects, and codec errors. RelaySession ingress reports repair-assisted decodes and exact FEC-recovered objects separately. It also reports the number of missing source symbols that RaptorQ reconstructed. Warm relays retain at most four objects, 2,048 datagrams, and 4 MiB for each child. Promotion replays only source datagrams with valid object deadlines. -
/metricsexposes Prometheus metrics for topology, telemetry freshness, node capacity, edge traffic, errors, stream lag, alerts, and transport settings. It also exposes cache-mesh FEC outcomes and per-node LL-HLS latency histograms. Scrapes use the same bounded telemetry snapshot as/api/mesh.
Run the UK node:
cargo run -- \
--region uk \
--node-id uk-1 \
--mesh-bind 127.0.0.1:9101 \
--peer 127.0.0.1:9201 \
--http-port 9444 \
--playback-base-url https://127.0.0.1:9444/live \
--fec-bind 127.0.0.1:12001 \
--media-fec-bind 127.0.0.1:12101 \
--telemetry-bind 127.0.0.1:7300 \
--provision-command 'echo provision node=$AV_MESH_PROVISION_NODE_ID region=$AV_MESH_PROVISION_REGION'For private-subnet discovery on Linode VLANs or any 10.x subnet, enable the feature. Bind mesh UDP to an address that the subnet can reach:
cargo run --features private-subnet-discovery -- \
--mesh-bind 0.0.0.0:9101 \
--private-subnet-discovery \
--private-discovery-broadcast-port 12345 \
--private-discovery-mesh-port 9101--peer remains useful as a seed list. Private discovery adds peers at runtime
from UDP broadcast announcements, then normal mesh HELLO frames gossip peer
addresses. Telemetry fills in the observable node topology for the operator UI.
For Linode-backed provisioning and private-subnet discovery together:
cargo run --features linode-private-discovery -- \
--region uk \
--mesh-bind 0.0.0.0:9101 \
--private-subnet-discovery \
--linode-provision \
--linode-image-id linode/arch \
--linode-instance-type g6-dedicated-2 \
--linode-domain-id "$LINODE_DOMAIN_ID" \
--linode-vlan-tag av-mesh \
--linode-region-map uk=gb-lon \
--linode-region-map us=us-east \
--provision-command "$(pwd)/scripts/bootstrap-linode-node.sh"The Linode provisioner reads API credentials from LINODE_API_TOKEN and
LINODE_PUB_KEY by default. If a --provision-command is also configured, it
runs after a successful Linode API provision and receives
AV_MESH_LINODE_INSTANCE_ID, AV_MESH_LINODE_PUBLIC_IPV4,
AV_MESH_LINODE_PRIVATE_IPAM, AV_MESH_LINODE_DNS_NAME, and
AV_MESH_LINODE_VLAN_LABEL.
scripts/bootstrap-linode-node.sh waits for SSH and syncs the local Wavey
workspace required by the path dependencies. It builds av-mesh on the node
with private-subnet discovery and installs a systemd service. It reuses or
generates shared local TLS material for TCP changes telemetry. Run it with
AV_MESH_BOOTSTRAP_DRY_RUN=1 to inspect the remote actions without connecting.
Run the US node:
cargo run -- \
--region us \
--node-id us-1 \
--mesh-bind 127.0.0.1:9201 \
--peer 127.0.0.1:9101 \
--http-port 9445 \
--fec-bind 127.0.0.1:12002 \
--media-fec-bind 127.0.0.1:12102 \
--telemetry-bind 127.0.0.1:7301 \
--telemetry-peer 127.0.0.1:7300To qualify the FEC snapshot lane locally, make the UK process the collector by
adding --telemetry-fec-bind 127.0.0.1:7350, then add this to the US process:
--telemetry-fec-target 127.0.0.1:7350The FEC lane and TCP changes lane can run together during rollout. Snapshot
collection and serialization remain on one 5-second producer. A full TCP queue
or UDP socket drops telemetry work instead of applying backpressure. Add
--telemetry-snapshots-fec-only on a sending node after comparison. This stops
TCP snapshot publication but leaves the TLS/TCP channel available for AVMC
control commands.
Publish MPEG-TS bytes over UDP-FEC into the UK mesh byte socket:
ffmpeg -re -f lavfi -i testsrc=size=1280x720:rate=30 \
-f lavfi -i sine=frequency=1000:sample_rate=48000 \
-c:v libx264 -preset veryfast -tune zerolatency \
-c:a aac -f mpegts - | \
cargo run --manifest-path ../av-contrib/Cargo.toml --bin udp-fec-send -- 127.0.0.1:12001Or run a contributor frontend for HTTP/RIST uploads and point it at the UK mesh FEC sockets:
cargo run --manifest-path ../av-contrib/Cargo.toml --bin av-contrib -- \
--http-port 9443 \
--mesh-fec-target 127.0.0.1:12001 \
--mesh-media-fec-target 127.0.0.1:12101 \
--rist-bind 127.0.0.1:7000Then publish over RIST with a RIST-capable sender such as OBS:
- URL:
rist://127.0.0.1:7000 - Profile:
main - Flow ID:
0x72737401
Or with the included stdin RIST sender:
ffmpeg -re -f lavfi -i testsrc=size=1280x720:rate=30 \
-f lavfi -i sine=frequency=1000:sample_rate=48000 \
-c:v libx264 -preset veryfast -tune zerolatency \
-c:a aac -f mpegts - | \
cargo run --manifest-path ../av-contrib/Cargo.toml --bin rist-send -- 127.0.0.1:7000Or publish MPEG-TS bytes over HTTP to the contributor frontend:
ffmpeg -re -f lavfi -i testsrc=size=1280x720:rate=30 \
-f lavfi -i sine=frequency=1000:sample_rate=48000 \
-c:v libx264 -preset veryfast -tune zerolatency \
-c:a aac -f mpegts - | \
curl -k -X POST --data-binary @- https://127.0.0.1:9443/ingestOr publish a non-TS media access unit through the contributor frontend. The
contributor wraps the access unit for the media UDP-FEC socket. The mesh stores
the raptorq-datagram-fec media header followed by the access-unit payload, so
the same stream can replicate over cache mesh on demand:
printf 'h264-access-unit-bytes' | \
curl -k -X POST --data-binary @- \
'https://127.0.0.1:9443/media/access-unit?stream_id=55&sequence=0&codec=h264&pts_ms=0&duration_ms=33&keyframe=true'
curl -k https://127.0.0.1:9444/media/55/unit/0 --output unit.avmauOr publish the same kind of non-TS access unit over the RaptorQ media/FEC datagram path:
printf 'h264-access-unit-bytes' | \
cargo run --manifest-path ../av-contrib/Cargo.toml --bin media-fec-send -- \
--stream-id 55 \
--sequence 1 \
--codec h264 \
--pts-ms 33 \
--duration-ms 33 \
--keyframe \
127.0.0.1:12101Then read either region:
- UK default master playlist:
https://127.0.0.1:9444/live/master.m3u8 - UK stream-specific master playlist for playlist/stream id 1:
https://127.0.0.1:9444/live/1/master.m3u8 - UK media playlist for playlist/stream id 1:
https://127.0.0.1:9444/live/1/stream.m3u8 - UK LLHLS tail for playlist/stream id 1:
https://127.0.0.1:9444/live/1/tail?mode=part - UK mesh UI:
https://127.0.0.1:9444/mesh - UK player:
https://127.0.0.1:9444/1 - US default master playlist:
https://127.0.0.1:9445/live/master.m3u8 - US stream-specific master playlist for playlist/stream id 1:
https://127.0.0.1:9445/live/1/master.m3u8 - Health:
https://127.0.0.1:9444/up - Stats:
https://127.0.0.1:9444/api/stats - Mesh snapshot:
https://127.0.0.1:9444/api/mesh - Mesh event stream:
https://127.0.0.1:9444/api/mesh/events - Edge WebSocket: opt in with
--edge-websocket, then usewss://127.0.0.1:9444/ws/mesh. - Edge WebTransport: opt in with
--edge-webtransport. Media datagrams can be raw RQD2 orraptorq-fec-transportstream-prefixed RQD2. - Edge HTTP/3: opt in with
--edge-http3for HLS and other HTTP routes. - Mesh/control raw TCP: opt in with
--raw-tcp-port <port>. Each request and response is framed as[u32_be length][payload]. Payloads can be JSON mesh protocol requests or serialized media access units. Add--raw-tcp-tlsto wrap the raw TCP listener with the same TLS material. - Control messages:
POST /api/control/provision-node,POST /api/control/close-node, andPOST /api/control/warm-streamoriginate commands locally. Nodes with a telemetry publisher forward those commands asAVMCframes over TCP changes. Telemetry peers ingest matchingAVMCframes and execute them by region or node id.
For av-llhls, pass the selected node's playback_base_url as baseUrl and
the decimal-string playlist/stream id as streamId. The blocking tail route is
/live/<stream_id>/tail?after=<sequence>. It waits on the exact next cache
commit without a polling sleep. A bounded wait can return 204 when the stream
has not arrived. That request also creates mesh demand.
The cache and replication unit remains --part-ms (or
AV_LL_HLS_PART_MS). Set --response-ms or AV_LL_HLS_RESPONSE_MS to make
the tail combine consecutive units by default. For example, 5 ms cache units
and AV_LL_HLS_RESPONSE_MS=200 return 40 ordered units per blocking response.
Controlled clients can override the service default with parts=<count> for
an A/B test. Counts are bounded to 200 and to the configured retained cache
capacity. Startup rejects a service default that cannot fit.
The response includes
x-sequence-start, x-sequence-end, x-part-count,
x-part-duration-ms, and x-response-duration-ms. x-sequence is the final
sequence and is the cursor for the next request. The body is the byte-exact
concatenation of the cached units, so the selected bytestream must be
self-delimiting when more than one unit is returned.
Synchronized clients can instead request
/live/tail-bundle?streams=1,2,3,4,5,6,7,8&from=<sequence>&parts=1.
The bounded NTB1 response carries the exact requested sequence range for
every stream or returns no partial bundle. The implementation resolves one
cache generation for each range and uses exact stream and sequence waiters. It
serves prevalidated Bytes slices from an indexed canonical slot. It retains
the complete canonical envelope for conflict and replication checks.
Per-track waits run sequentially under one shared absolute deadline. Every registration rechecks the cache, so arbitrary track arrival order cannot create a lost wakeup or extend the request budget.
RelaySession recovery now carries the exact FEC-reconstructed envelope beside
the already bounded, payload-hash-verified MediaObject. The cache can commit
that pair directly instead of encoding it twice and decoding/hashing it again.
This does not bypass canonical parsing, announcement-key rebinding, payload
integrity, replay checks, or immutable cache conflict checks. A playback leaf
with no downstream relay or audio subscriber also discards AEP1 traffic after
the magic check. Forwarding relays and active subscribers retain full
validation.
The matched private-GCP profile and its strict non-pass are recorded in
Needletail's 19 July tail-bundle report.
The latest retained run delivered 2,304,000/2,304,000 parts and reduced edge
host CPU to 34.765%. However, 9 of 288,000 bundles exceeded 20 ms. Treat this
result as an optimization result, not an endurance or production-sizing claim.
The server uses the local TLS material from av-service. Clients will need to
trust that cert or use an insecure local test client.
With a local or deployed contributor-plus-mesh stack already running, collect repeatable client and service latency measurements with:
make realtime-benchmarkThe benchmark sends raw contributor payloads and reads each configured mesh
playlist at configurable concurrency. It prefers persistent HTTP/2 sessions
through h2load. It uses parallel curl processes as a fallback. It reports
client p50, p95, p99, mean, maximum, and effective request rate. Duration mode
(DURATION_SECONDS) sustains load instead of stopping at a request count.
PROPAGATION_PROBES posts unique canaries. It measures when each edge can fetch
the exact bytes. Before-and-after Prometheus snapshots give service histogram
count and p95 changes. Contributor results attribute interval p95 to bounded
encode_wait, encode, send, and telemetry stages. The terminal and
RESULT_JSON show these results, which identify the hot path of a forwarding
regression. RESULT_JSON stores machine-readable evidence.
Override topology and load through CONTRIB_URL, comma-separated MESH_URLS,
CONCURRENCY, H2_STREAMS_PER_CLIENT, PAYLOAD_BYTES, and LOAD_CLIENT. Set
PARALLEL_ENDPOINTS=1 to load the contributor and each edge at the same time.
CONTRIB_METRICS_URL and MESH_METRICS_URLS can select private monitoring
endpoints for service histogram scrapes. Client load can continue to use public
origins.
Budgets are explicit and opt-in so a laptop result is not presented as a global SLO. For example:
INGEST_P95_BUDGET_MS=15 PLAYLIST_P95_BUDGET_MS=10 \
SAMPLES=1000 CONCURRENCY=32 make realtime-benchmarkAny unexpected HTTP status, missing metrics surface, or p95 budget violation fails the command.
For the repeatable two-region qualification, including real packet impairment:
make realtime-qualificationThis runs baseline and impaired phases against the same release stack. The mesh
profile applies 35±5 ms one-way delay with 1% loss. The contributor-FEC profile
applies 10±2 ms delay with 1% loss. Both profiles use the unprivileged
udp-netem binary.
The test verifies that both links carried and dropped packets. It rejects
emulator overflow and send errors. It checks client and service p95 budgets. The
impaired phase must recover source symbols without FEC decode errors. The test
writes qualification artifacts under target/realtime-qualification/.
Default gates are 15 ms contributor ingest p95 and 5 ms playlist p95. They also include 1 ms edge-handler p95 and 200 ms propagation p95. The impaired-to-baseline p95 ratio must not exceed 3. You can override all profile and latency budgets with environment variables.
For an authorized deployed canary, run repeated simultaneous windows and preserve round-level plus whole-soak evidence with:
CONTRIB_URL=https://contrib-canary.example \
MESH_URLS=https://uk-canary.example,https://us-canary.example \
SOAK_SECONDS=3600 ROUND_SECONDS=60 \
make realtime-soakThe soak defaults to verified TLS, 8 HTTP/2 connections × 4 streams per endpoint, exact-byte propagation probes, and the provisional local latency gates. It fails on any bad round or counter reset. It also fails on new pipeline errors, MPEG-TS continuity errors, or expired FEC objects. An explicit limit can override the corresponding failure.
Each run writes raw metric snapshots, per-round
logs/JSON, counter deltas, cross-round percentiles, and soak.json under
target/realtime-soak/. Use a dedicated canary stream and explicitly scoped
hosts. The script never deploys or changes servers.
observability/ contains a runnable Prometheus, Alertmanager, and Grafana
bundle. It stores the metrics that av-contrib and av-mesh expose. It records
forwarding-stage and mesh-handler p95 values. It charts FEC recovery and wire
overhead. It evaluates bounded-label alerts and gives diagnosis guidance.
make observability-check
docker compose -f observability/compose.yml up -dGrafana is served at http://127.0.0.1:3000/d/wavey-realtime, Prometheus at
http://127.0.0.1:9090, and Alertmanager at http://127.0.0.1:9093. The local
scrape configuration trusts the development certificates insecurely. Deployed
scrapers must use the deployment CA. Alert thresholds are labeled
slo: provisional until a hardware-, geography-, bitrate-, and load-qualified
regional soak establishes production SLOs. See observability/README.md for
deployment and notification-routing notes.
The product UI lives in ../needletail/mission-control. Its Leptos/WASM app
consumes av-mesh /api/mesh and av-contrib /api/status using bounded,
Serde-default snapshot models.
make mission-control-check
make mission-control-build
make mission-control-serveBy default it points at the local stack endpoints:
https://local.infidelity.io:19444/api/mesh and
https://local.infidelity.io:19443/api/status.
For local live-ingest testing with both playback edges and the contributor ingress under one Rust supervisor, use Needletail:
make local-stackThe supervisor builds release av-mesh, release ../av-contrib, and
Needletail Operations. It passes the product assets to each playback edge with
NEEDLETAIL_MISSION_CONTROL_DIST. It uses the Infidelity wildcard TLS material from
../tls/local.infidelity.io. It starts UK and US mesh nodes and one av-contrib
ingress. It prefixes each child process output line with its source.
By default, it uses stream ID 1, UK egress
https://local.infidelity.io:19444/live/1/master.m3u8, US egress
https://local.infidelity.io:19445/live/1/master.m3u8, and Operations at
/mesh on both ports. Any compatible sender can publish SRT to
srt://local.infidelity.io:27001?mode=caller or RIST to
local.infidelity.io:27000 with main profile and flow id 0x11223344. RTMP
compatibility remains available at rtmp://local.infidelity.io:19350/live with
stream key live-local. The
supervisor defaults the LL-HLS part target to 50 ms, accepts
AV_LL_HLS_PART_MS or --part-ms overrides, and shells out to curl for local
health checks.
Useful overrides:
PART_MS=67 \
RUST_LOG=av_mesh=trace,av_contrib=trace,rtmp_ingress=debug \
STACK_ARGS="--rtmp-bind 127.0.0.1:19351 --srt-bind 127.0.0.1:27011" \
make local-stack STREAM_ID=4294967351 HOST=local.infidelity.ioUse --cert and --key to point at alternate PEM files. The default hostname
must resolve to loopback. On this machine local.infidelity.io resolves to
127.0.0.1 and ::1.
Use --mission-control-dist /path/to/dist to reuse specific product assets.
Use --no-mission-control-build with an existing build. /mesh returns concise
setup guidance while the asset path is being prepared. --no-build reuses the
component release binaries. Run make help for direct playback-edge and Mission
Control tasks.
Use k3d for the local orchestration smoke path. It runs k3s nodes in Docker, which keeps the test close to edge Kubernetes while still being disposable on a developer machine.
Prerequisites:
brew install k3d kubectl
# Docker Desktop, Colima, or another Docker-compatible runtime must be running.Build the local image, create a two-node k3d cluster, deploy UK/US mesh nodes, and start port-forwards:
make k3d-upThe smoke script builds deploy/k3d/Dockerfile and imports av-mesh:local into
the cluster. It generates a short-lived TLS secret and applies
deploy/k3d/av-mesh.yaml. It waits for both deployments. It then checks /up
and /api/mesh through local port-forwards.
Useful follow-up commands:
make k3d-check
kubectl -n av-mesh get pods,svc
make k3d-downThe services already use tracing_subscriber with RUST_LOG env filters. The
current detailed logs are mostly info! and debug!. Setting trace is
accepted, but only code paths with trace! calls will emit extra trace-level
events. The next refactor should provide a same-process Tokio harness. Expose
library run(config, shutdown) entry points from av-mesh and av-contrib.
Then, make this supervisor call those tasks directly instead of supervising
child binaries.
For a quick automated check, run:
scripts/two-region-smoke.shThe smoke script builds the binaries and generates short-lived local TLS
material for tcp-changes. It starts UK and US nodes and a UK av-contrib
frontend on local high ports. It configures the RaptorQ cache mesh. It sends
distinct HTTP, UDP-FEC, and RIST contributor payloads into the UK node. Several
concurrent clients then read the exact HLS parts from both nodes. Finally, the
script sends a UK warm-stream command to the US node through the AVMT/AVMC
TCP changes path.
This is not the final multi-protocol edge deployment yet. The current milestone proves the shared-cache behavior needed by the requested mesh:
- A node can discover configured peers with UDP-FEC
HELLOframes, gossip those peer addresses through the mesh, and with the optional private-subnet discovery feature it can add 10.x subnet peers discovered by UDP broadcast. - A node can ingest opaque stream bytes from UDP-FEC or a contributor frontend
such as
av-contrib, then write media parts to aplaylists::ChunkCache. - Peers replicate those slots over the shared Wavey RaptorQ-FEC datagram protocol, then serve them as HLS parts.
- Region and continent identity are explicit, starting with
ukandus. - The first replication planner tests local global distributions, demand signals, storage capacity, and anti-affinity so close nodes are not selected as redundant mirrors when better placements exist.
- Playlist demand and warm-stream controls broadcast replica requests over the mesh transport, and peers with the requested stream send cached slots back to the requester.
- The telemetry/UI path exposes local snapshots through HTTPS and can publish them through either the compatibility TCP changes feed or the bounded RaptorQ UDP lane. Both feed one central aggregate node/connection/capacity/edge-service view.
- The runtime planner exposes planned replicas through
/api/meshand asks for the stream when the local node is selected as a baseline replica. - UI/API/raw TCP controls publish
AVMCcommand frames over TCP changes when a telemetry feed is available. Nodes apply warm-stream commands for their region/node id and mark themselves draining on matching close-node commands.
Next, harden the bootstrap image path. Remove the remaining contributor-ingest
smoke paths from av-mesh after av-contrib owns those fixtures end to end.
av-mesh is available under the MIT License.