From 61b8900929254f3088cffbf06a82fb06798b8008 Mon Sep 17 00:00:00 2001 From: Ryan Gaus Date: Wed, 11 Nov 2026 12:52:38 +1300 Subject: [PATCH] Add DataPacket sender oneof for packets sent on behalf of another participant The SFU leaves participant_identity as the attributed participant when an agent sends a text stream header or chat message on someone else's behalf, but stamps the agent's identity on the stream's chunks and trailer. Receivers can't tell which participant to expect the rest of the stream from. The new sender oneof carries the real sender in those cases: sender_identity, or sender_hidden when that sender is a hidden participant. Only the SFU sets it (from server protocol 20). --- .changeset/data-packet-sender-identity.md | 6 ++ livekit/livekit_models.pb.go | 69 +++++++++++++++++++++-- protobufs/livekit_models.proto | 14 ++++- 3 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 .changeset/data-packet-sender-identity.md diff --git a/.changeset/data-packet-sender-identity.md b/.changeset/data-packet-sender-identity.md new file mode 100644 index 000000000..1a719425d --- /dev/null +++ b/.changeset/data-packet-sender-identity.md @@ -0,0 +1,6 @@ +--- +"github.com/livekit/protocol": patch +"@livekit/protocol": patch +--- + +Add a `sender` oneof (`sender_identity` / `sender_hidden`) to `DataPacket`, which the SFU sets to the real sender when an agent sends a packet on behalf of another participant diff --git a/livekit/livekit_models.pb.go b/livekit/livekit_models.pb.go index 8c5ffd328..6a50cea3e 100644 --- a/livekit/livekit_models.pb.go +++ b/livekit/livekit_models.pb.go @@ -3493,8 +3493,18 @@ type DataPacket struct { Sequence uint32 `protobuf:"varint,16,opt,name=sequence,proto3" json:"sequence,omitempty"` // sid of the user that sent the message ParticipantSid string `protobuf:"bytes,17,opt,name=participant_sid,json=participantSid,proto3" json:"participant_sid,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // The participant that actually sent this packet, when `participant_identity` names a different + // participant (an agent sending a text stream or chat message on behalf of another participant). + // Set only by the SFU, which clears any value a client sends; unset when `participant_identity` is + // the real sender. Available from server protocol 20. + // + // Types that are valid to be assigned to Sender: + // + // *DataPacket_SenderIdentity + // *DataPacket_SenderHidden + Sender isDataPacket_Sender `protobuf_oneof:"sender"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *DataPacket) Reset() { @@ -3688,6 +3698,31 @@ func (x *DataPacket) GetParticipantSid() string { return "" } +func (x *DataPacket) GetSender() isDataPacket_Sender { + if x != nil { + return x.Sender + } + return nil +} + +func (x *DataPacket) GetSenderIdentity() string { + if x != nil { + if x, ok := x.Sender.(*DataPacket_SenderIdentity); ok { + return x.SenderIdentity + } + } + return "" +} + +func (x *DataPacket) GetSenderHidden() bool { + if x != nil { + if x, ok := x.Sender.(*DataPacket_SenderHidden); ok { + return x.SenderHidden + } + } + return false +} + type isDataPacket_Value interface { isDataPacket_Value() } @@ -3771,6 +3806,25 @@ func (*DataPacket_StreamTrailer) isDataPacket_Value() {} func (*DataPacket_EncryptedPacket) isDataPacket_Value() {} +type isDataPacket_Sender interface { + isDataPacket_Sender() +} + +type DataPacket_SenderIdentity struct { + // identity of the participant that actually sent this packet + SenderIdentity string `protobuf:"bytes,19,opt,name=sender_identity,json=senderIdentity,proto3,oneof"` +} + +type DataPacket_SenderHidden struct { + // the participant that actually sent this packet is hidden, so its identity is withheld; only + // ever true + SenderHidden bool `protobuf:"varint,20,opt,name=sender_hidden,json=senderHidden,proto3,oneof"` +} + +func (*DataPacket_SenderIdentity) isDataPacket_Sender() {} + +func (*DataPacket_SenderHidden) isDataPacket_Sender() {} + type EncryptedPacket struct { state protoimpl.MessageState `protogen:"open.v1"` EncryptionType Encryption_Type `protobuf:"varint,1,opt,name=encryption_type,json=encryptionType,proto3,enum=livekit.Encryption_Type" json:"encryption_type,omitempty"` @@ -7182,7 +7236,7 @@ const file_livekit_models_proto_rawDesc = "" + "\vMODE_UNUSED\x10\x00\x12 \n" + "\x1cONE_SPATIAL_LAYER_PER_STREAM\x10\x01\x12&\n" + "\"MULTIPLE_SPATIAL_LAYERS_PER_STREAM\x10\x02\x123\n" + - "/ONE_SPATIAL_LAYER_PER_STREAM_INCOMPLETE_RTCP_SR\x10\x03\"\x8c\b\n" + + "/ONE_SPATIAL_LAYER_PER_STREAM_INCOMPLETE_RTCP_SR\x10\x03\"\xe8\b\n" + "\n" + "DataPacket\x120\n" + "\x04kind\x18\x01 \x01(\x0e2\x18.livekit.DataPacket.KindB\x02\x18\x01R\x04kind\x121\n" + @@ -7204,11 +7258,14 @@ const file_livekit_models_proto_rawDesc = "" + "\x0estream_trailer\x18\x0f \x01(\v2\x1b.livekit.DataStream.TrailerH\x00R\rstreamTrailer\x12E\n" + "\x10encrypted_packet\x18\x12 \x01(\v2\x18.livekit.EncryptedPacketH\x00R\x0fencryptedPacket\x12\x1a\n" + "\bsequence\x18\x10 \x01(\rR\bsequence\x12'\n" + - "\x0fparticipant_sid\x18\x11 \x01(\tR\x0eparticipantSid\"\x1f\n" + + "\x0fparticipant_sid\x18\x11 \x01(\tR\x0eparticipantSid\x12)\n" + + "\x0fsender_identity\x18\x13 \x01(\tH\x01R\x0esenderIdentity\x12%\n" + + "\rsender_hidden\x18\x14 \x01(\bH\x01R\fsenderHidden\"\x1f\n" + "\x04Kind\x12\f\n" + "\bRELIABLE\x10\x00\x12\t\n" + "\x05LOSSY\x10\x01B\a\n" + - "\x05value\"\xaa\x01\n" + + "\x05valueB\b\n" + + "\x06sender\"\xaa\x01\n" + "\x0fEncryptedPacket\x12A\n" + "\x0fencryption_type\x18\x01 \x01(\x0e2\x18.livekit.Encryption.TypeR\x0eencryptionType\x12\x0e\n" + "\x02iv\x18\x02 \x01(\fR\x02iv\x12\x1b\n" + @@ -7917,6 +7974,8 @@ func file_livekit_models_proto_init() { (*DataPacket_StreamChunk)(nil), (*DataPacket_StreamTrailer)(nil), (*DataPacket_EncryptedPacket)(nil), + (*DataPacket_SenderIdentity)(nil), + (*DataPacket_SenderHidden)(nil), } file_livekit_models_proto_msgTypes[22].OneofWrappers = []any{ (*EncryptedPacketPayload_User)(nil), diff --git a/protobufs/livekit_models.proto b/protobufs/livekit_models.proto index 1e681696b..eb554041b 100644 --- a/protobufs/livekit_models.proto +++ b/protobufs/livekit_models.proto @@ -471,7 +471,19 @@ message DataPacket { // sid of the user that sent the message string participant_sid = 17; - // NEXT_ID: 19 + // The participant that actually sent this packet, when `participant_identity` names a different + // participant (an agent sending a text stream or chat message on behalf of another participant). + // Set only by the SFU, which clears any value a client sends; unset when `participant_identity` is + // the real sender. Available from server protocol 20. + oneof sender { + // identity of the participant that actually sent this packet + string sender_identity = 19; + // the participant that actually sent this packet is hidden, so its identity is withheld; only + // ever true + bool sender_hidden = 20; + } + + // NEXT_ID: 21 } message EncryptedPacket {