Follow-up to #506, deliberately parked.
Today. GET /cameras/:id/streams returns RTSP URLs with the server's single
global go2rtc restreamer username/password embedded in the authority
(rtsp://user:pass@host:18554/<name>). That credential pair is server-wide,
covers every stream go2rtc serves, and does not expire. #506 narrows who can
ask for it (a full login session only, no scoped media token), which is the
right minimum for v0.2.0, but the credential handed out is still broader and
more durable than the request that earned it.
The fuller fix. Mint a short-lived, per-request RTSP credential scoped to the
one stream the caller asked for, so the response body carries nothing durable and
the "what you get back matches what you proved" property holds structurally
rather than by careful endpoint-by-endpoint review.
Why it is not in the v0.2.0 change. It is a real design task, not an
extractor swap:
- go2rtc's auth model needs investigating — whether per-stream or ephemeral
credentials can be expressed at all without Crumb hand-managing
go2rtc.yaml, which the reconcile loop owns (see AGENTS.md, "go2rtc streams
are managed at runtime").
- Credential lifetime has to outlive a long-running live view, or the desktop
and mobile walls drop streams mid-session. That likely means a renewal path in
three clients, not just a server change.
- It touches the live-view path on every client, so it wants its own testing
pass rather than riding along with a hardening PR.
Revisit triggers. Any of: go2rtc gains a usable per-stream or token auth
mechanism; Crumb grows a deployment mode where the RTSP endpoint is reachable
beyond the LAN; or another endpoint is found returning a credential broader than
its caller's proof, making the one-off extractor gate feel like the wrong shape.
Rationale and the accepted trade-off are recorded in docs/DECISIONS.md.
Follow-up to #506, deliberately parked.
Today.
GET /cameras/:id/streamsreturns RTSP URLs with the server's singleglobal go2rtc restreamer username/password embedded in the authority
(
rtsp://user:pass@host:18554/<name>). That credential pair is server-wide,covers every stream go2rtc serves, and does not expire. #506 narrows who can
ask for it (a full login session only, no scoped media token), which is the
right minimum for v0.2.0, but the credential handed out is still broader and
more durable than the request that earned it.
The fuller fix. Mint a short-lived, per-request RTSP credential scoped to the
one stream the caller asked for, so the response body carries nothing durable and
the "what you get back matches what you proved" property holds structurally
rather than by careful endpoint-by-endpoint review.
Why it is not in the v0.2.0 change. It is a real design task, not an
extractor swap:
credentials can be expressed at all without Crumb hand-managing
go2rtc.yaml, which the reconcile loop owns (see AGENTS.md, "go2rtc streamsare managed at runtime").
and mobile walls drop streams mid-session. That likely means a renewal path in
three clients, not just a server change.
pass rather than riding along with a hardening PR.
Revisit triggers. Any of: go2rtc gains a usable per-stream or token auth
mechanism; Crumb grows a deployment mode where the RTSP endpoint is reachable
beyond the LAN; or another endpoint is found returning a credential broader than
its caller's proof, making the one-off extractor gate feel like the wrong shape.
Rationale and the accepted trade-off are recorded in
docs/DECISIONS.md.