Three documents asserted that the re-stream heuristic just needs a consumer
because `ClientIP` and friends "already flow through streammonitor". Planning
Batch 6 against the actual code showed that is false, and it is exactly the
class of overstatement this audit exists to correct.
`ClientIP` is one value per session, not a set:
- Integrated mode stamps it at session creation and no media request updates
it — `internal/api/handlers/stream.go` never touches `ClientIP`.
- The edge tracker overwrites `records[sessionID]` on every `Track` /
`EnsureEphemeral`, so concurrent pours leave only the last writer's address.
- `mergeStreams` then collapses multi-node records to a single winner and only
backfills an address when the winner has none.
So several devices pulling one session present as a single address, and a
consumer polling the existing snapshot cannot see fan-out at all. Detecting it
requires bounded viewer observations collected at authenticated media-serve
time.
Also records two limits worth knowing before anyone scopes this again:
- An IP-based signal can only catch shared session-URL fan-out (C16). C14 —
a downstream proxy re-broadcasting one pulled stream — is invisible by
construction, because Silo sees exactly one address no matter how wide the
fan-out. C14 stays scored as no defense.
- `internal/proxy/server.go`'s `edgeClientIP` reads `RemoteAddr` and ignores
`X-Forwarded-For`, unlike the native surface which uses the trusted-proxy
resolver in `internal/clientip`. Behind ingress every edge viewer collapses
to the ingress address, so the signal is blind there until that is fixed.
No behavior change; documentation only.
Part of #305