diff --git a/pkg/media/live_window.go b/pkg/media/live_window.go index 543d71102..bc9bc8659 100644 --- a/pkg/media/live_window.go +++ b/pkg/media/live_window.go @@ -58,7 +58,17 @@ func (mm *MediaManager) GetLiveWindow(did string) *livehls.Writer { // validated segment — local or replicated — so a node serves live HLS for any // stream whose segments flow through its ValidateMP4. Best-effort: window // errors are logged, never fatal to ingest. -func (mm *MediaManager) feedLiveWindow(ctx context.Context, did string, segment []byte) { +// +// Only PUBLISHED segments are folded in. Live HLS requests are unauthenticated +// today, so a pre-live (unpublished) segment in the window would be watchable by +// anyone, not just the streamer. Until HLS gains per-viewer auth we withhold +// pre-live HLS entirely: an unfed window stays nil, and the getLive* handlers +// return StreamNotLive. The streamer still monitors their own pre-live stream +// over WebRTC, which gates playback on viewer == streamer. +func (mm *MediaManager) feedLiveWindow(ctx context.Context, did string, segment []byte, published bool) { + if !published { + return + } eventCh := make(chan *muxl.MuxlEvent, 8) errCh := make(chan error, 1) go func() { diff --git a/pkg/media/validate.go b/pkg/media/validate.go index 528dd66ac..83097e835 100644 --- a/pkg/media/validate.go +++ b/pkg/media/validate.go @@ -200,7 +200,8 @@ func (mm *MediaManager) distributeSegment(ctx context.Context, vs *validatedSegm // critical path. This runs for every segment — locally signed or replicated // from another node — so any node that validates a stream's segments can // serve its live HLS. WithoutCancel keeps the feed alive past this request. - go mm.feedLiveWindow(context.WithoutCancel(ctx), vs.repoDID, seg) + // Only published segments are actually folded in (see feedLiveWindow). + go mm.feedLiveWindow(context.WithoutCancel(ctx), vs.repoDID, seg, meta.Published) var deleteAfter *time.Time if meta.DistributionPolicy != nil && meta.DistributionPolicy.DeleteAfterSeconds != nil { diff --git a/pkg/media/validate_bare_test.go b/pkg/media/validate_bare_test.go index f36b2beb7..f254f0760 100644 --- a/pkg/media/validate_bare_test.go +++ b/pkg/media/validate_bare_test.go @@ -127,7 +127,14 @@ func TestFeedLiveWindow(t *testing.T) { require.NotEmpty(t, m4s) mm := &MediaManager{liveWindows: map[string]*livehls.Writer{}} - mm.feedLiveWindow(ctx, "did:test:streamer", m4s) + + // Pre-live (unpublished) segments must NOT be folded into the live window — + // live HLS is unauthenticated, so anything in the window is world-readable. + mm.feedLiveWindow(ctx, "did:test:streamer", m4s, false) + require.Nil(t, mm.GetLiveWindow("did:test:streamer"), + "unpublished segment must not create a live-HLS window") + + mm.feedLiveWindow(ctx, "did:test:streamer", m4s, true) w := mm.GetLiveWindow("did:test:streamer") require.NotNil(t, w, "window created on feed")