diff --git a/.changeset/room-rpc-sip.md b/.changeset/room-rpc-sip.md new file mode 100644 index 000000000..bd9c2f5fe --- /dev/null +++ b/.changeset/room-rpc-sip.md @@ -0,0 +1,6 @@ +--- +"github.com/livekit/protocol": minor +"@livekit/protocol": minor +--- + +Introduce a participant RPC protobuf registry for well-known LiveKit RPCs. diff --git a/livekit/roomrpc/siprpc/roomrpc_sip_v1.pb.go b/livekit/roomrpc/siprpc/roomrpc_sip_v1.pb.go new file mode 100644 index 000000000..6a7b4ef86 --- /dev/null +++ b/livekit/roomrpc/siprpc/roomrpc_sip_v1.pb.go @@ -0,0 +1,202 @@ +// Copyright 2026 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v7.35.0 +// source: roomrpc/siprpc/roomrpc_sip_v1.proto + +package siprpc + +import ( + _ "github.com/livekit/protocol/livekit/logger" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GetRemoteHeadersV1Request struct { + state protoimpl.MessageState `protogen:"open.v1"` + Include []string `protobuf:"bytes,1,rep,name=include,proto3" json:"include,omitempty"` // list of headers to include + Exclude []string `protobuf:"bytes,2,rep,name=exclude,proto3" json:"exclude,omitempty"` // list of headers to exclude + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetRemoteHeadersV1Request) Reset() { + *x = GetRemoteHeadersV1Request{} + mi := &file_roomrpc_siprpc_roomrpc_sip_v1_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetRemoteHeadersV1Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRemoteHeadersV1Request) ProtoMessage() {} + +func (x *GetRemoteHeadersV1Request) ProtoReflect() protoreflect.Message { + mi := &file_roomrpc_siprpc_roomrpc_sip_v1_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRemoteHeadersV1Request.ProtoReflect.Descriptor instead. +func (*GetRemoteHeadersV1Request) Descriptor() ([]byte, []int) { + return file_roomrpc_siprpc_roomrpc_sip_v1_proto_rawDescGZIP(), []int{0} +} + +func (x *GetRemoteHeadersV1Request) GetInclude() []string { + if x != nil { + return x.Include + } + return nil +} + +func (x *GetRemoteHeadersV1Request) GetExclude() []string { + if x != nil { + return x.Exclude + } + return nil +} + +type GetRemoteHeadersV1Response struct { + state protoimpl.MessageState `protogen:"open.v1"` + Headers map[string]string `protobuf:"bytes,1,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetRemoteHeadersV1Response) Reset() { + *x = GetRemoteHeadersV1Response{} + mi := &file_roomrpc_siprpc_roomrpc_sip_v1_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetRemoteHeadersV1Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRemoteHeadersV1Response) ProtoMessage() {} + +func (x *GetRemoteHeadersV1Response) ProtoReflect() protoreflect.Message { + mi := &file_roomrpc_siprpc_roomrpc_sip_v1_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRemoteHeadersV1Response.ProtoReflect.Descriptor instead. +func (*GetRemoteHeadersV1Response) Descriptor() ([]byte, []int) { + return file_roomrpc_siprpc_roomrpc_sip_v1_proto_rawDescGZIP(), []int{1} +} + +func (x *GetRemoteHeadersV1Response) GetHeaders() map[string]string { + if x != nil { + return x.Headers + } + return nil +} + +var File_roomrpc_siprpc_roomrpc_sip_v1_proto protoreflect.FileDescriptor + +const file_roomrpc_siprpc_roomrpc_sip_v1_proto_rawDesc = "" + + "\n" + + "#roomrpc/siprpc/roomrpc_sip_v1.proto\x12\alivekit\x1a\x14logger/options.proto\"O\n" + + "\x19GetRemoteHeadersV1Request\x12\x18\n" + + "\ainclude\x18\x01 \x03(\tR\ainclude\x12\x18\n" + + "\aexclude\x18\x02 \x03(\tR\aexclude\"\xca\x01\n" + + "\x1aGetRemoteHeadersV1Response\x12p\n" + + "\aheaders\x18\x01 \x03(\v20.livekit.GetRemoteHeadersV1Response.HeadersEntryB$\xb2P\x1e\xc0P\x01R\aheaders\x1a:\n" + + "\fHeadersEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x012o\n" + + "\x10SIPParticipantV1\x12[\n" + + "\x10GetRemoteHeaders\x12\".livekit.GetRemoteHeadersV1Request\x1a#.livekit.GetRemoteHeadersV1ResponseBnZ2github.com/livekit/protocol/livekit/roomrpc/siprpc\xaa\x02\x19LiveKit.Proto.RoomRPC.SIP\xea\x02\x1bLiveKit::Proto::RoomRPC:SIPb\x06proto3" + +var ( + file_roomrpc_siprpc_roomrpc_sip_v1_proto_rawDescOnce sync.Once + file_roomrpc_siprpc_roomrpc_sip_v1_proto_rawDescData []byte +) + +func file_roomrpc_siprpc_roomrpc_sip_v1_proto_rawDescGZIP() []byte { + file_roomrpc_siprpc_roomrpc_sip_v1_proto_rawDescOnce.Do(func() { + file_roomrpc_siprpc_roomrpc_sip_v1_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_roomrpc_siprpc_roomrpc_sip_v1_proto_rawDesc), len(file_roomrpc_siprpc_roomrpc_sip_v1_proto_rawDesc))) + }) + return file_roomrpc_siprpc_roomrpc_sip_v1_proto_rawDescData +} + +var file_roomrpc_siprpc_roomrpc_sip_v1_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_roomrpc_siprpc_roomrpc_sip_v1_proto_goTypes = []any{ + (*GetRemoteHeadersV1Request)(nil), // 0: livekit.GetRemoteHeadersV1Request + (*GetRemoteHeadersV1Response)(nil), // 1: livekit.GetRemoteHeadersV1Response + nil, // 2: livekit.GetRemoteHeadersV1Response.HeadersEntry +} +var file_roomrpc_siprpc_roomrpc_sip_v1_proto_depIdxs = []int32{ + 2, // 0: livekit.GetRemoteHeadersV1Response.headers:type_name -> livekit.GetRemoteHeadersV1Response.HeadersEntry + 0, // 1: livekit.SIPParticipantV1.GetRemoteHeaders:input_type -> livekit.GetRemoteHeadersV1Request + 1, // 2: livekit.SIPParticipantV1.GetRemoteHeaders:output_type -> livekit.GetRemoteHeadersV1Response + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_roomrpc_siprpc_roomrpc_sip_v1_proto_init() } +func file_roomrpc_siprpc_roomrpc_sip_v1_proto_init() { + if File_roomrpc_siprpc_roomrpc_sip_v1_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_roomrpc_siprpc_roomrpc_sip_v1_proto_rawDesc), len(file_roomrpc_siprpc_roomrpc_sip_v1_proto_rawDesc)), + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_roomrpc_siprpc_roomrpc_sip_v1_proto_goTypes, + DependencyIndexes: file_roomrpc_siprpc_roomrpc_sip_v1_proto_depIdxs, + MessageInfos: file_roomrpc_siprpc_roomrpc_sip_v1_proto_msgTypes, + }.Build() + File_roomrpc_siprpc_roomrpc_sip_v1_proto = out.File + file_roomrpc_siprpc_roomrpc_sip_v1_proto_goTypes = nil + file_roomrpc_siprpc_roomrpc_sip_v1_proto_depIdxs = nil +} diff --git a/magefile.go b/magefile.go index 3fdeda1ea..d7220a18f 100644 --- a/magefile.go +++ b/magefile.go @@ -94,8 +94,16 @@ func Proto() error { "rpc/sip.proto", } + // mapped proto directory: + // ./protobufs/livekit/roomrpc/rpc + // and generated Go package: + // ./livekit/roomrpc/rpc + roomrpcTypeNames := []string{ + "sip", + } + fmt.Println("generating protobuf") - target := "livekit" + const target = "./livekit" if err := os.MkdirAll(target, 0755); err != nil { return err } @@ -162,6 +170,29 @@ func Proto() error { } } + fmt.Println("generating protobuf (livekit/roomrpc)") + for _, protoName := range roomrpcTypeNames { + pkgName := "roomrpc/" + protoName + "rpc" + protoFiles, err := os.ReadDir(filepath.Join("./protobufs", pkgName)) + if err != nil { + return err + } + args := []string{ + "--go_out", target, + "--go_opt=paths=source_relative", + "--plugin=go=" + protocGoPath, + "-I=./protobufs", + } + for _, protoFile := range protoFiles { + args = append(args, filepath.Join(pkgName, protoFile.Name())) + } + cmd := exec.Command(protoc, args...) + connectStd(cmd) + if err := cmd.Run(); err != nil { + return err + } + } + fmt.Println("generating grpc protobuf") args = append([]string{ "--go_out", ".", diff --git a/protobufs/roomrpc/README.md b/protobufs/roomrpc/README.md new file mode 100644 index 000000000..9ffe7816f --- /dev/null +++ b/protobufs/roomrpc/README.md @@ -0,0 +1,25 @@ +# LiveKit participant RPCs registry (roomrpc) + +These protos describe a list of well-known [LiveKit participant RPCs](https://docs.livekit.io/transport/data/rpc/) +and corresponding request/response messages. + +## Directory structure + +Each participant type or API should have its own directory under `roomrpc` with the name `rpc` +to avoid Go package name collisions and isolate types for each API. + +## Versioning + +Proto files should follow the name convention: `roomrpc__v.proto`. Version is a single sequential number, not a semver. +For example: `roomrpc_sip_v1.proto`. + +Corresponding `V` should be added as a suffix to the service definition and the request/response types +(method name should omit the version, as the versioned service name is part of the method path). + +```protobuf +service MyServiceV1 { + rpc DoSomething(DoSomethingV1Request) returns (DoSomethingV1Response); +} +message DoSomethingV1Request {} +message DoSomethingV1Response {} +``` \ No newline at end of file diff --git a/protobufs/roomrpc/siprpc/roomrpc_sip_v1.proto b/protobufs/roomrpc/siprpc/roomrpc_sip_v1.proto new file mode 100644 index 000000000..38f18f74a --- /dev/null +++ b/protobufs/roomrpc/siprpc/roomrpc_sip_v1.proto @@ -0,0 +1,38 @@ +// Copyright 2026 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package livekit; +option go_package = "github.com/livekit/protocol/livekit/roomrpc/siprpc"; +option csharp_namespace = "LiveKit.Proto.RoomRPC.SIP"; +option ruby_package = "LiveKit::Proto::RoomRPC:SIP"; + +import "logger/options.proto"; + +service SIPParticipantV1 { + // RPC: lk.sip.GetRemoteHeaders + rpc GetRemoteHeaders(GetRemoteHeadersV1Request) returns (GetRemoteHeadersV1Response); +} + +message GetRemoteHeadersV1Request { + repeated string include = 1; // list of headers to include + repeated string exclude = 2; // list of headers to exclude +} +message GetRemoteHeadersV1Response { + map headers = 1 [ + (logger.sensitivity) = SENSITIVITY_PII, + (logger.redact_format) = "" + ]; +} \ No newline at end of file