Actually let you attach a live stream to a story

Same gap as iOS: §6.13 shipped the wire format, the model, and the viewer's
consent gate, with no way to enter a URL. Adds the field to the composer, with
the disclosure said when attaching rather than only when watching.

⚠️ Post is disabled for a URL that could not be disclosed — a destination with
no host cannot be named, and a viewer cannot consent to one they cannot be
shown.

⚠️ The poster's own copy carries the stream too, so the story does not look
different to the person who sent it than to everyone who receives it.

engine 177, client 96, app 46.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Paul Hitt
2026-08-29 14:10:25 -04:00
co-authored by Claude Opus 4.8
parent 1bbf8b460b
commit 4441400cd2
4 changed files with 67 additions and 6 deletions
@@ -226,6 +226,15 @@ class Conversations(
toUserIds: List<Long>,
lifetimeSeconds: Double = STORY_LIFETIME_SECONDS,
duration: Double = 0.0,
/**
* A live stream this story embeds (§6.13).
*
* ⚠️ When set, [data] is the POSTER — a still the viewer sees before
* deciding whether to connect — not the stream. A stream cannot be
* sealed: it is continuous and hosted elsewhere, so watching it means
* the viewer's device contacts that host directly.
*/
streamUrl: String? = null,
): StoryPosted {
val sealed = Attachment.seal(data)
val id = relay.uploadAttachment(sealed.blob)
@@ -235,7 +244,7 @@ class Conversations(
duration = duration,
expires = System.currentTimeMillis() / 1000.0 + lifetimeSeconds,
)
val body = Content.attachment(ref, caption, null)
val body = Content.attachment(ref, caption, null, streamUrl = streamUrl)
var reached = 0
var missed = 0