# ECP-0121: Greedy Multi-Relay Streams

Status: Draft

## Problem statement

Live streams should be publishable to more than one relay at once so viewers can pick the fastest
path and regional relays can mirror each other. The current public directory shape only exposes one
`relay_url` per stream, which encourages duplicate publishers when we want redundancy. For OTA
sources, duplicate publishers are dangerous because each one opens another tuner read.

## Constraints

- Preserve the existing `relay_url`, `broadcast_name`, and `track_name` fields for deployed web,
  archive, and manual watch links.
- Keep those primary fields as the compatibility contract; `relays[]` is additive and optional for
  consumers.
- Do not duplicate HDHomeRun source reads to get multi-region relay presence.
- Let the public directory advertise multiple relays before every consumer implements racing.
- Keep rollback simple: clients can ignore `relays[]` and keep using the primary legacy fields.

## Decision

Add an ordered `relays[]` candidate list to public stream entries and stream upserts. Stream upserts
continue to require the primary legacy fields; the first relay is mirrored into those fields and
remains the primary/default path. `control-bridge-web` now forwards all relay transports already
present in a control announcement instead of flattening to the first relay only. Current consumers
can keep reading the legacy fields until they explicitly add relay racing.

The intended next step is a single ingest/fanout publisher: read the source once, encode/fragment
once, publish the same stream objects to LAX and NYC relay sessions, and optionally let relays mirror
to each other. Consumers can then race candidates greedily by availability/latency without causing
extra source reads.

## Alternatives considered

- Start one publisher per relay. Rejected because it duplicates source reads and can exhaust physical
  tuners, which was the LA outage failure mode.
- Replace the legacy fields with `relays[]`. Rejected because deployed clients and archive workers
  already depend on the single-relay shape.
- Accept `relays[]` without primary legacy fields. Rejected because that would make rollback depend
  on every publisher being downgraded at the same time as the directory.
- Wait for full relay racing before changing the directory. Rejected because exposing the ordered
  candidate set is a small compatible step that unblocks incremental consumers.

## Rollout / teardown plan

Deploy the compatible schema first. Then add publisher fanout and consumer relay racing behind
separate flags. Teardown is removing `relays[]` from upserts and consumers; legacy primary-field
behavior remains intact throughout.
