diff --git a/buf.gen.yaml b/buf.gen.yaml
index 94df2c83b60ce4644177169ae9b236922dcbe398..79efe75afbefc2df14f30f322efd62a4b0b983f4 100644
--- a/buf.gen.yaml
+++ b/buf.gen.yaml
@@ -18,3 +18,7 @@ plugins:
     out: gen/python
   - plugin: buf.build/protocolbuffers/python:v25.0
     out: gen/python
+  - plugin: buf.build/grpc/java:v1.59.0
+    out: gen/java
+  - plugin: buf.build/protocolbuffers/java:v25.0
+    out: gen/java
diff --git a/gen/go/quicsep/quicsep.pb.go b/gen/go/quicsep/quicsep.pb.go
index 032c5f53346ce4fed5fcf20ed4eb82fddee4cfaa..e2e49e956d3342d58c98ecb6146aa6f3cd981dde 100644
--- a/gen/go/quicsep/quicsep.pb.go
+++ b/gen/go/quicsep/quicsep.pb.go
@@ -28,9 +28,12 @@ type Metadata struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
-	Vendor    string `protobuf:"bytes,2,opt,name=vendor,proto3" json:"vendor,omitempty"`
-	Version   string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"`
+	Timestamp         int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Vendor            string `protobuf:"bytes,2,opt,name=vendor,proto3" json:"vendor,omitempty"`
+	Version           string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"`
+	KeyGenerationRate uint64 `protobuf:"varint,4,opt,name=keyGenerationRate,proto3" json:"keyGenerationRate,omitempty"`
+	SourceId          string `protobuf:"bytes,5,opt,name=sourceId,proto3" json:"sourceId,omitempty"`
+	DestinationId     string `protobuf:"bytes,6,opt,name=destinationId,proto3" json:"destinationId,omitempty"`
 }
 
 func (x *Metadata) Reset() {
@@ -86,6 +89,27 @@ func (x *Metadata) GetVersion() string {
 	return ""
 }
 
+func (x *Metadata) GetKeyGenerationRate() uint64 {
+	if x != nil {
+		return x.KeyGenerationRate
+	}
+	return 0
+}
+
+func (x *Metadata) GetSourceId() string {
+	if x != nil {
+		return x.SourceId
+	}
+	return ""
+}
+
+func (x *Metadata) GetDestinationId() string {
+	if x != nil {
+		return x.DestinationId
+	}
+	return ""
+}
+
 type KeyBulk struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -170,7 +194,8 @@ type CapabilitiesRequest struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
+	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Version   string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
 }
 
 func (x *CapabilitiesRequest) Reset() {
@@ -205,6 +230,13 @@ func (*CapabilitiesRequest) Descriptor() ([]byte, []int) {
 	return file_quicsep_quicsep_proto_rawDescGZIP(), []int{2}
 }
 
+func (x *CapabilitiesRequest) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
 func (x *CapabilitiesRequest) GetVersion() string {
 	if x != nil {
 		return x.Version
@@ -217,7 +249,8 @@ type CapabilitiesResponse struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
+	Timestamp int64  `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Version   string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
 }
 
 func (x *CapabilitiesResponse) Reset() {
@@ -252,6 +285,13 @@ func (*CapabilitiesResponse) Descriptor() ([]byte, []int) {
 	return file_quicsep_quicsep_proto_rawDescGZIP(), []int{3}
 }
 
+func (x *CapabilitiesResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
 func (x *CapabilitiesResponse) GetVersion() string {
 	if x != nil {
 		return x.Version
@@ -555,95 +595,107 @@ var file_quicsep_quicsep_proto_rawDesc = []byte{
 	0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63,
 	0x2e, 0x76, 0x31, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
 	0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x22, 0x5a, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1c, 0x0a, 0x09,
-	0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
-	0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x65,
-	0x6e, 0x64, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x76, 0x65, 0x6e, 0x64,
-	0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xc5, 0x01, 0x0a,
-	0x07, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6c, 0x6b, 0x12, 0x1c, 0x0a, 0x05, 0x6b, 0x65, 0x79, 0x49,
-	0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52,
-	0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x04, 0x6b, 0x65,
-	0x79, 0x73, 0x12, 0x24, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18,
-	0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x09, 0x6b,
-	0x65, 0x79, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x20, 0x0a, 0x07, 0x6b, 0x65, 0x79, 0x48,
-	0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01,
-	0x01, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x48, 0x61, 0x73, 0x68, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65,
-	0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x71,
-	0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
-	0x74, 0x61, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61,
-	0x64, 0x61, 0x74, 0x61, 0x22, 0x37, 0x0a, 0x13, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69,
-	0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x76,
-	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48,
-	0x03, 0xc8, 0x01, 0x01, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x38, 0x0a,
-	0x14, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x07,
-	0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x6e, 0x0a, 0x0f, 0x50, 0x75, 0x73, 0x68, 0x4b,
-	0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x09, 0x74, 0x69,
-	0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x06, 0xba,
-	0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
-	0x12, 0x35, 0x0a, 0x07, 0x6b, 0x65, 0x79, 0x42, 0x75, 0x6c, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x13, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x2e, 0x4b,
-	0x65, 0x79, 0x42, 0x75, 0x6c, 0x6b, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x07,
-	0x6b, 0x65, 0x79, 0x42, 0x75, 0x6c, 0x6b, 0x22, 0x38, 0x0a, 0x10, 0x50, 0x75, 0x73, 0x68, 0x4b,
-	0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x74,
-	0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x06,
-	0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
-	0x70, 0x22, 0x3b, 0x0a, 0x13, 0x51, 0x6b, 0x64, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
-	0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65,
+	0x22, 0xca, 0x01, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1c, 0x0a,
+	0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03,
+	0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x76,
+	0x65, 0x6e, 0x64, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x76, 0x65, 0x6e,
+	0x64, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a,
+	0x11, 0x6b, 0x65, 0x79, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x61,
+	0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x6b, 0x65, 0x79, 0x47, 0x65, 0x6e,
+	0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73,
+	0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73,
+	0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x64, 0x65, 0x73, 0x74, 0x69,
+	0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
+	0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xc5, 0x01,
+	0x0a, 0x07, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6c, 0x6b, 0x12, 0x1c, 0x0a, 0x05, 0x6b, 0x65, 0x79,
+	0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01,
+	0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x04, 0x6b,
+	0x65, 0x79, 0x73, 0x12, 0x24, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x09,
+	0x6b, 0x65, 0x79, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x20, 0x0a, 0x07, 0x6b, 0x65, 0x79,
+	0x48, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8,
+	0x01, 0x01, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x48, 0x61, 0x73, 0x68, 0x12, 0x38, 0x0a, 0x08, 0x6d,
+	0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e,
+	0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64,
+	0x61, 0x74, 0x61, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74,
+	0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5d, 0x0a, 0x13, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c,
+	0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x09,
+	0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42,
+	0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+	0x6d, 0x70, 0x12, 0x20, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x07, 0x76, 0x65, 0x72,
+	0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5e, 0x0a, 0x14, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69,
+	0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x09,
+	0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42,
+	0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+	0x6d, 0x70, 0x12, 0x20, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x07, 0x76, 0x65, 0x72,
+	0x73, 0x69, 0x6f, 0x6e, 0x22, 0x6e, 0x0a, 0x0f, 0x50, 0x75, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x73,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73,
+	0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8,
+	0x01, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x35, 0x0a,
+	0x07, 0x6b, 0x65, 0x79, 0x42, 0x75, 0x6c, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13,
+	0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x42,
+	0x75, 0x6c, 0x6b, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x07, 0x6b, 0x65, 0x79,
+	0x42, 0x75, 0x6c, 0x6b, 0x22, 0x38, 0x0a, 0x10, 0x50, 0x75, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x73,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65,
 	0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x06, 0xba, 0x48, 0x03,
-	0xc8, 0x01, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x76,
-	0x0a, 0x14, 0x51, 0x6b, 0x64, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
-	0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01,
-	0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x38, 0x0a, 0x08,
-	0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14,
-	0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61,
-	0x64, 0x61, 0x74, 0x61, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x08, 0x6d, 0x65,
-	0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x4d, 0x0a, 0x1b, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f,
-	0x77, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77,
-	0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba,
-	0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52,
-	0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x1e, 0x0a, 0x1c, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77,
-	0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xfd, 0x02, 0x0a, 0x1b, 0x4b, 0x6d, 0x73, 0x51, 0x6b, 0x64,
-	0x6d, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65,
-	0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x53, 0x0a, 0x0c, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c,
-	0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e,
-	0x76, 0x31, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63,
-	0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73,
-	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x08, 0x50, 0x75,
-	0x73, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x1b, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63,
-	0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31,
-	0x2e, 0x50, 0x75, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0c, 0x51, 0x6b, 0x64, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64,
-	0x61, 0x74, 0x61, 0x12, 0x1f, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31,
-	0x2e, 0x51, 0x6b, 0x64, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76,
-	0x31, 0x2e, 0x51, 0x6b, 0x64, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x14, 0x53, 0x68, 0x75, 0x74,
-	0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-	0x12, 0x27, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68,
-	0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
-	0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x71, 0x75, 0x69, 0x70,
-	0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e,
-	0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
-	0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xa0, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x71, 0x75,
-	0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x51, 0x75, 0x69, 0x63, 0x73, 0x65,
-	0x70, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x66,
-	0x62, 0x69, 0x2e, 0x68, 0x2d, 0x64, 0x61, 0x2e, 0x64, 0x65, 0x2f, 0x64, 0x61, 0x6e, 0x65, 0x74,
-	0x2f, 0x71, 0x75, 0x69, 0x63, 0x73, 0x65, 0x70, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f,
-	0x71, 0x75, 0x69, 0x63, 0x73, 0x65, 0x70, 0x3b, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x76,
-	0x31, 0xa2, 0x02, 0x03, 0x51, 0x58, 0x58, 0xaa, 0x02, 0x0a, 0x51, 0x75, 0x69, 0x70, 0x73, 0x65,
-	0x63, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0a, 0x51, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x5c, 0x56,
-	0x31, 0xe2, 0x02, 0x16, 0x51, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x5c, 0x56, 0x31, 0x5c, 0x47,
-	0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0b, 0x51, 0x75, 0x69,
-	0x70, 0x73, 0x65, 0x63, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0xc8, 0x01, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3b,
+	0x0a, 0x13, 0x51, 0x6b, 0x64, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+	0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01,
+	0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x76, 0x0a, 0x14, 0x51,
+	0x6b, 0x64, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x09,
+	0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74,
+	0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x71, 0x75,
+	0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
+	0x61, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64,
+	0x61, 0x74, 0x61, 0x22, 0x4d, 0x0a, 0x1b, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e,
+	0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65,
+	0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8,
+	0x01, 0x01, 0x52, 0x0e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x61, 0x73,
+	0x6f, 0x6e, 0x22, 0x1e, 0x0a, 0x1c, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x6f,
+	0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x32, 0xfd, 0x02, 0x0a, 0x1b, 0x4b, 0x6d, 0x73, 0x51, 0x6b, 0x64, 0x6d, 0x43, 0x6f,
+	0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69,
+	0x63, 0x65, 0x12, 0x53, 0x0a, 0x0c, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69,
+	0x65, 0x73, 0x12, 0x1f, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x2e,
+	0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31,
+	0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x08, 0x50, 0x75, 0x73, 0x68, 0x4b,
+	0x65, 0x79, 0x73, 0x12, 0x1b, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31,
+	0x2e, 0x50, 0x75, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x1c, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75,
+	0x73, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
+	0x12, 0x53, 0x0a, 0x0c, 0x51, 0x6b, 0x64, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+	0x12, 0x1f, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x6b,
+	0x64, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x1a, 0x20, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x2e, 0x51,
+	0x6b, 0x64, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x14, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77,
+	0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e,
+	0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64,
+	0x6f, 0x77, 0x6e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63,
+	0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x6f, 0x74, 0x69,
+	0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x22, 0x00, 0x42, 0xa0, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x71, 0x75, 0x69, 0x70, 0x73,
+	0x65, 0x63, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x51, 0x75, 0x69, 0x63, 0x73, 0x65, 0x70, 0x50, 0x72,
+	0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x66, 0x62, 0x69, 0x2e,
+	0x68, 0x2d, 0x64, 0x61, 0x2e, 0x64, 0x65, 0x2f, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x71, 0x75,
+	0x69, 0x63, 0x73, 0x65, 0x70, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x71, 0x75, 0x69,
+	0x63, 0x73, 0x65, 0x70, 0x3b, 0x71, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x76, 0x31, 0xa2, 0x02,
+	0x03, 0x51, 0x58, 0x58, 0xaa, 0x02, 0x0a, 0x51, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x2e, 0x56,
+	0x31, 0xca, 0x02, 0x0a, 0x51, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x5c, 0x56, 0x31, 0xe2, 0x02,
+	0x16, 0x51, 0x75, 0x69, 0x70, 0x73, 0x65, 0x63, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d,
+	0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0b, 0x51, 0x75, 0x69, 0x70, 0x73, 0x65,
+	0x63, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/gen/java/com/quipsec/v1/CapabilitiesRequest.java b/gen/java/com/quipsec/v1/CapabilitiesRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..28bbfaf4d39dc6cbb97f45e49d642d6d4f0020d4
--- /dev/null
+++ b/gen/java/com/quipsec/v1/CapabilitiesRequest.java
@@ -0,0 +1,609 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+/**
+ * Protobuf type {@code quipsec.v1.CapabilitiesRequest}
+ */
+public final class CapabilitiesRequest extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:quipsec.v1.CapabilitiesRequest)
+    CapabilitiesRequestOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use CapabilitiesRequest.newBuilder() to construct.
+  private CapabilitiesRequest(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private CapabilitiesRequest() {
+    version_ = "";
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new CapabilitiesRequest();
+  }
+
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_CapabilitiesRequest_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_CapabilitiesRequest_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.quipsec.v1.CapabilitiesRequest.class, com.quipsec.v1.CapabilitiesRequest.Builder.class);
+  }
+
+  public static final int TIMESTAMP_FIELD_NUMBER = 1;
+  private long timestamp_ = 0L;
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+   * @return The timestamp.
+   */
+  @java.lang.Override
+  public long getTimestamp() {
+    return timestamp_;
+  }
+
+  public static final int VERSION_FIELD_NUMBER = 2;
+  @SuppressWarnings("serial")
+  private volatile java.lang.Object version_ = "";
+  /**
+   * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+   * @return The version.
+   */
+  @java.lang.Override
+  public java.lang.String getVersion() {
+    java.lang.Object ref = version_;
+    if (ref instanceof java.lang.String) {
+      return (java.lang.String) ref;
+    } else {
+      com.google.protobuf.ByteString bs = 
+          (com.google.protobuf.ByteString) ref;
+      java.lang.String s = bs.toStringUtf8();
+      version_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+   * @return The bytes for version.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getVersionBytes() {
+    java.lang.Object ref = version_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      version_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (timestamp_ != 0L) {
+      output.writeInt64(1, timestamp_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 2, version_);
+    }
+    getUnknownFields().writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (timestamp_ != 0L) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeInt64Size(1, timestamp_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, version_);
+    }
+    size += getUnknownFields().getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.quipsec.v1.CapabilitiesRequest)) {
+      return super.equals(obj);
+    }
+    com.quipsec.v1.CapabilitiesRequest other = (com.quipsec.v1.CapabilitiesRequest) obj;
+
+    if (getTimestamp()
+        != other.getTimestamp()) return false;
+    if (!getVersion()
+        .equals(other.getVersion())) return false;
+    if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    hash = (37 * hash) + TIMESTAMP_FIELD_NUMBER;
+    hash = (53 * hash) + com.google.protobuf.Internal.hashLong(
+        getTimestamp());
+    hash = (37 * hash) + VERSION_FIELD_NUMBER;
+    hash = (53 * hash) + getVersion().hashCode();
+    hash = (29 * hash) + getUnknownFields().hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.quipsec.v1.CapabilitiesRequest parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.CapabilitiesRequest parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.CapabilitiesRequest parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.CapabilitiesRequest parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.CapabilitiesRequest parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.CapabilitiesRequest parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.CapabilitiesRequest parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.CapabilitiesRequest parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  public static com.quipsec.v1.CapabilitiesRequest parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+
+  public static com.quipsec.v1.CapabilitiesRequest parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.quipsec.v1.CapabilitiesRequest parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.CapabilitiesRequest parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.quipsec.v1.CapabilitiesRequest prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code quipsec.v1.CapabilitiesRequest}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:quipsec.v1.CapabilitiesRequest)
+      com.quipsec.v1.CapabilitiesRequestOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_CapabilitiesRequest_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_CapabilitiesRequest_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.quipsec.v1.CapabilitiesRequest.class, com.quipsec.v1.CapabilitiesRequest.Builder.class);
+    }
+
+    // Construct using com.quipsec.v1.CapabilitiesRequest.newBuilder()
+    private Builder() {
+
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      bitField0_ = 0;
+      timestamp_ = 0L;
+      version_ = "";
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_CapabilitiesRequest_descriptor;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.CapabilitiesRequest getDefaultInstanceForType() {
+      return com.quipsec.v1.CapabilitiesRequest.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.CapabilitiesRequest build() {
+      com.quipsec.v1.CapabilitiesRequest result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.CapabilitiesRequest buildPartial() {
+      com.quipsec.v1.CapabilitiesRequest result = new com.quipsec.v1.CapabilitiesRequest(this);
+      if (bitField0_ != 0) { buildPartial0(result); }
+      onBuilt();
+      return result;
+    }
+
+    private void buildPartial0(com.quipsec.v1.CapabilitiesRequest result) {
+      int from_bitField0_ = bitField0_;
+      if (((from_bitField0_ & 0x00000001) != 0)) {
+        result.timestamp_ = timestamp_;
+      }
+      if (((from_bitField0_ & 0x00000002) != 0)) {
+        result.version_ = version_;
+      }
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.quipsec.v1.CapabilitiesRequest) {
+        return mergeFrom((com.quipsec.v1.CapabilitiesRequest)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.quipsec.v1.CapabilitiesRequest other) {
+      if (other == com.quipsec.v1.CapabilitiesRequest.getDefaultInstance()) return this;
+      if (other.getTimestamp() != 0L) {
+        setTimestamp(other.getTimestamp());
+      }
+      if (!other.getVersion().isEmpty()) {
+        version_ = other.version_;
+        bitField0_ |= 0x00000002;
+        onChanged();
+      }
+      this.mergeUnknownFields(other.getUnknownFields());
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            case 8: {
+              timestamp_ = input.readInt64();
+              bitField0_ |= 0x00000001;
+              break;
+            } // case 8
+            case 18: {
+              version_ = input.readStringRequireUtf8();
+              bitField0_ |= 0x00000002;
+              break;
+            } // case 18
+            default: {
+              if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                done = true; // was an endgroup tag
+              }
+              break;
+            } // default:
+          } // switch (tag)
+        } // while (!done)
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.unwrapIOException();
+      } finally {
+        onChanged();
+      } // finally
+      return this;
+    }
+    private int bitField0_;
+
+    private long timestamp_ ;
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @return The timestamp.
+     */
+    @java.lang.Override
+    public long getTimestamp() {
+      return timestamp_;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @param value The timestamp to set.
+     * @return This builder for chaining.
+     */
+    public Builder setTimestamp(long value) {
+
+      timestamp_ = value;
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearTimestamp() {
+      bitField0_ = (bitField0_ & ~0x00000001);
+      timestamp_ = 0L;
+      onChanged();
+      return this;
+    }
+
+    private java.lang.Object version_ = "";
+    /**
+     * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+     * @return The version.
+     */
+    public java.lang.String getVersion() {
+      java.lang.Object ref = version_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        version_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+     * @return The bytes for version.
+     */
+    public com.google.protobuf.ByteString
+        getVersionBytes() {
+      java.lang.Object ref = version_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        version_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+     * @param value The version to set.
+     * @return This builder for chaining.
+     */
+    public Builder setVersion(
+        java.lang.String value) {
+      if (value == null) { throw new NullPointerException(); }
+      version_ = value;
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearVersion() {
+      version_ = getDefaultInstance().getVersion();
+      bitField0_ = (bitField0_ & ~0x00000002);
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+     * @param value The bytes for version to set.
+     * @return This builder for chaining.
+     */
+    public Builder setVersionBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) { throw new NullPointerException(); }
+      checkByteStringIsUtf8(value);
+      version_ = value;
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:quipsec.v1.CapabilitiesRequest)
+  }
+
+  // @@protoc_insertion_point(class_scope:quipsec.v1.CapabilitiesRequest)
+  private static final com.quipsec.v1.CapabilitiesRequest DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.quipsec.v1.CapabilitiesRequest();
+  }
+
+  public static com.quipsec.v1.CapabilitiesRequest getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<CapabilitiesRequest>
+      PARSER = new com.google.protobuf.AbstractParser<CapabilitiesRequest>() {
+    @java.lang.Override
+    public CapabilitiesRequest parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      Builder builder = newBuilder();
+      try {
+        builder.mergeFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(builder.buildPartial());
+      } catch (com.google.protobuf.UninitializedMessageException e) {
+        throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(e)
+            .setUnfinishedMessage(builder.buildPartial());
+      }
+      return builder.buildPartial();
+    }
+  };
+
+  public static com.google.protobuf.Parser<CapabilitiesRequest> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<CapabilitiesRequest> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.quipsec.v1.CapabilitiesRequest getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+
diff --git a/gen/java/com/quipsec/v1/CapabilitiesRequestOrBuilder.java b/gen/java/com/quipsec/v1/CapabilitiesRequestOrBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..04f71f03f0706585738f7c51c53b54d08d851b60
--- /dev/null
+++ b/gen/java/com/quipsec/v1/CapabilitiesRequestOrBuilder.java
@@ -0,0 +1,28 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+public interface CapabilitiesRequestOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:quipsec.v1.CapabilitiesRequest)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+   * @return The timestamp.
+   */
+  long getTimestamp();
+
+  /**
+   * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+   * @return The version.
+   */
+  java.lang.String getVersion();
+  /**
+   * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+   * @return The bytes for version.
+   */
+  com.google.protobuf.ByteString
+      getVersionBytes();
+}
diff --git a/gen/java/com/quipsec/v1/CapabilitiesResponse.java b/gen/java/com/quipsec/v1/CapabilitiesResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4e0dd86aebb0c2b5044ab49acb5df85a1bfceba
--- /dev/null
+++ b/gen/java/com/quipsec/v1/CapabilitiesResponse.java
@@ -0,0 +1,609 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+/**
+ * Protobuf type {@code quipsec.v1.CapabilitiesResponse}
+ */
+public final class CapabilitiesResponse extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:quipsec.v1.CapabilitiesResponse)
+    CapabilitiesResponseOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use CapabilitiesResponse.newBuilder() to construct.
+  private CapabilitiesResponse(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private CapabilitiesResponse() {
+    version_ = "";
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new CapabilitiesResponse();
+  }
+
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_CapabilitiesResponse_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_CapabilitiesResponse_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.quipsec.v1.CapabilitiesResponse.class, com.quipsec.v1.CapabilitiesResponse.Builder.class);
+  }
+
+  public static final int TIMESTAMP_FIELD_NUMBER = 1;
+  private long timestamp_ = 0L;
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+   * @return The timestamp.
+   */
+  @java.lang.Override
+  public long getTimestamp() {
+    return timestamp_;
+  }
+
+  public static final int VERSION_FIELD_NUMBER = 2;
+  @SuppressWarnings("serial")
+  private volatile java.lang.Object version_ = "";
+  /**
+   * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+   * @return The version.
+   */
+  @java.lang.Override
+  public java.lang.String getVersion() {
+    java.lang.Object ref = version_;
+    if (ref instanceof java.lang.String) {
+      return (java.lang.String) ref;
+    } else {
+      com.google.protobuf.ByteString bs = 
+          (com.google.protobuf.ByteString) ref;
+      java.lang.String s = bs.toStringUtf8();
+      version_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+   * @return The bytes for version.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getVersionBytes() {
+    java.lang.Object ref = version_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      version_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (timestamp_ != 0L) {
+      output.writeInt64(1, timestamp_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 2, version_);
+    }
+    getUnknownFields().writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (timestamp_ != 0L) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeInt64Size(1, timestamp_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, version_);
+    }
+    size += getUnknownFields().getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.quipsec.v1.CapabilitiesResponse)) {
+      return super.equals(obj);
+    }
+    com.quipsec.v1.CapabilitiesResponse other = (com.quipsec.v1.CapabilitiesResponse) obj;
+
+    if (getTimestamp()
+        != other.getTimestamp()) return false;
+    if (!getVersion()
+        .equals(other.getVersion())) return false;
+    if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    hash = (37 * hash) + TIMESTAMP_FIELD_NUMBER;
+    hash = (53 * hash) + com.google.protobuf.Internal.hashLong(
+        getTimestamp());
+    hash = (37 * hash) + VERSION_FIELD_NUMBER;
+    hash = (53 * hash) + getVersion().hashCode();
+    hash = (29 * hash) + getUnknownFields().hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.quipsec.v1.CapabilitiesResponse parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.CapabilitiesResponse parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.CapabilitiesResponse parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.CapabilitiesResponse parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.CapabilitiesResponse parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.CapabilitiesResponse parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.CapabilitiesResponse parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.CapabilitiesResponse parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  public static com.quipsec.v1.CapabilitiesResponse parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+
+  public static com.quipsec.v1.CapabilitiesResponse parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.quipsec.v1.CapabilitiesResponse parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.CapabilitiesResponse parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.quipsec.v1.CapabilitiesResponse prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code quipsec.v1.CapabilitiesResponse}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:quipsec.v1.CapabilitiesResponse)
+      com.quipsec.v1.CapabilitiesResponseOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_CapabilitiesResponse_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_CapabilitiesResponse_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.quipsec.v1.CapabilitiesResponse.class, com.quipsec.v1.CapabilitiesResponse.Builder.class);
+    }
+
+    // Construct using com.quipsec.v1.CapabilitiesResponse.newBuilder()
+    private Builder() {
+
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      bitField0_ = 0;
+      timestamp_ = 0L;
+      version_ = "";
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_CapabilitiesResponse_descriptor;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.CapabilitiesResponse getDefaultInstanceForType() {
+      return com.quipsec.v1.CapabilitiesResponse.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.CapabilitiesResponse build() {
+      com.quipsec.v1.CapabilitiesResponse result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.CapabilitiesResponse buildPartial() {
+      com.quipsec.v1.CapabilitiesResponse result = new com.quipsec.v1.CapabilitiesResponse(this);
+      if (bitField0_ != 0) { buildPartial0(result); }
+      onBuilt();
+      return result;
+    }
+
+    private void buildPartial0(com.quipsec.v1.CapabilitiesResponse result) {
+      int from_bitField0_ = bitField0_;
+      if (((from_bitField0_ & 0x00000001) != 0)) {
+        result.timestamp_ = timestamp_;
+      }
+      if (((from_bitField0_ & 0x00000002) != 0)) {
+        result.version_ = version_;
+      }
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.quipsec.v1.CapabilitiesResponse) {
+        return mergeFrom((com.quipsec.v1.CapabilitiesResponse)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.quipsec.v1.CapabilitiesResponse other) {
+      if (other == com.quipsec.v1.CapabilitiesResponse.getDefaultInstance()) return this;
+      if (other.getTimestamp() != 0L) {
+        setTimestamp(other.getTimestamp());
+      }
+      if (!other.getVersion().isEmpty()) {
+        version_ = other.version_;
+        bitField0_ |= 0x00000002;
+        onChanged();
+      }
+      this.mergeUnknownFields(other.getUnknownFields());
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            case 8: {
+              timestamp_ = input.readInt64();
+              bitField0_ |= 0x00000001;
+              break;
+            } // case 8
+            case 18: {
+              version_ = input.readStringRequireUtf8();
+              bitField0_ |= 0x00000002;
+              break;
+            } // case 18
+            default: {
+              if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                done = true; // was an endgroup tag
+              }
+              break;
+            } // default:
+          } // switch (tag)
+        } // while (!done)
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.unwrapIOException();
+      } finally {
+        onChanged();
+      } // finally
+      return this;
+    }
+    private int bitField0_;
+
+    private long timestamp_ ;
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @return The timestamp.
+     */
+    @java.lang.Override
+    public long getTimestamp() {
+      return timestamp_;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @param value The timestamp to set.
+     * @return This builder for chaining.
+     */
+    public Builder setTimestamp(long value) {
+
+      timestamp_ = value;
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearTimestamp() {
+      bitField0_ = (bitField0_ & ~0x00000001);
+      timestamp_ = 0L;
+      onChanged();
+      return this;
+    }
+
+    private java.lang.Object version_ = "";
+    /**
+     * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+     * @return The version.
+     */
+    public java.lang.String getVersion() {
+      java.lang.Object ref = version_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        version_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+     * @return The bytes for version.
+     */
+    public com.google.protobuf.ByteString
+        getVersionBytes() {
+      java.lang.Object ref = version_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        version_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+     * @param value The version to set.
+     * @return This builder for chaining.
+     */
+    public Builder setVersion(
+        java.lang.String value) {
+      if (value == null) { throw new NullPointerException(); }
+      version_ = value;
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearVersion() {
+      version_ = getDefaultInstance().getVersion();
+      bitField0_ = (bitField0_ & ~0x00000002);
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+     * @param value The bytes for version to set.
+     * @return This builder for chaining.
+     */
+    public Builder setVersionBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) { throw new NullPointerException(); }
+      checkByteStringIsUtf8(value);
+      version_ = value;
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:quipsec.v1.CapabilitiesResponse)
+  }
+
+  // @@protoc_insertion_point(class_scope:quipsec.v1.CapabilitiesResponse)
+  private static final com.quipsec.v1.CapabilitiesResponse DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.quipsec.v1.CapabilitiesResponse();
+  }
+
+  public static com.quipsec.v1.CapabilitiesResponse getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<CapabilitiesResponse>
+      PARSER = new com.google.protobuf.AbstractParser<CapabilitiesResponse>() {
+    @java.lang.Override
+    public CapabilitiesResponse parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      Builder builder = newBuilder();
+      try {
+        builder.mergeFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(builder.buildPartial());
+      } catch (com.google.protobuf.UninitializedMessageException e) {
+        throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(e)
+            .setUnfinishedMessage(builder.buildPartial());
+      }
+      return builder.buildPartial();
+    }
+  };
+
+  public static com.google.protobuf.Parser<CapabilitiesResponse> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<CapabilitiesResponse> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.quipsec.v1.CapabilitiesResponse getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+
diff --git a/gen/java/com/quipsec/v1/CapabilitiesResponseOrBuilder.java b/gen/java/com/quipsec/v1/CapabilitiesResponseOrBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..e7222e9353e12a05e2e04034bd34dfc814bd0124
--- /dev/null
+++ b/gen/java/com/quipsec/v1/CapabilitiesResponseOrBuilder.java
@@ -0,0 +1,28 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+public interface CapabilitiesResponseOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:quipsec.v1.CapabilitiesResponse)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+   * @return The timestamp.
+   */
+  long getTimestamp();
+
+  /**
+   * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+   * @return The version.
+   */
+  java.lang.String getVersion();
+  /**
+   * <code>string version = 2 [json_name = "version", (.buf.validate.field) = { ... }</code>
+   * @return The bytes for version.
+   */
+  com.google.protobuf.ByteString
+      getVersionBytes();
+}
diff --git a/gen/java/com/quipsec/v1/KeyBulk.java b/gen/java/com/quipsec/v1/KeyBulk.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad6a865e8be4694a8bde11656782d2446652267f
--- /dev/null
+++ b/gen/java/com/quipsec/v1/KeyBulk.java
@@ -0,0 +1,1005 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+/**
+ * Protobuf type {@code quipsec.v1.KeyBulk}
+ */
+public final class KeyBulk extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:quipsec.v1.KeyBulk)
+    KeyBulkOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use KeyBulk.newBuilder() to construct.
+  private KeyBulk(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private KeyBulk() {
+    keyId_ = "";
+    keys_ = com.google.protobuf.ByteString.EMPTY;
+    keyHash_ = "";
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new KeyBulk();
+  }
+
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_KeyBulk_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_KeyBulk_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.quipsec.v1.KeyBulk.class, com.quipsec.v1.KeyBulk.Builder.class);
+  }
+
+  private int bitField0_;
+  public static final int KEYID_FIELD_NUMBER = 1;
+  @SuppressWarnings("serial")
+  private volatile java.lang.Object keyId_ = "";
+  /**
+   * <code>string keyId = 1 [json_name = "keyId", (.buf.validate.field) = { ... }</code>
+   * @return The keyId.
+   */
+  @java.lang.Override
+  public java.lang.String getKeyId() {
+    java.lang.Object ref = keyId_;
+    if (ref instanceof java.lang.String) {
+      return (java.lang.String) ref;
+    } else {
+      com.google.protobuf.ByteString bs = 
+          (com.google.protobuf.ByteString) ref;
+      java.lang.String s = bs.toStringUtf8();
+      keyId_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string keyId = 1 [json_name = "keyId", (.buf.validate.field) = { ... }</code>
+   * @return The bytes for keyId.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getKeyIdBytes() {
+    java.lang.Object ref = keyId_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      keyId_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  public static final int KEYS_FIELD_NUMBER = 2;
+  private com.google.protobuf.ByteString keys_ = com.google.protobuf.ByteString.EMPTY;
+  /**
+   * <code>bytes keys = 2 [json_name = "keys", (.buf.validate.field) = { ... }</code>
+   * @return The keys.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString getKeys() {
+    return keys_;
+  }
+
+  public static final int KEYLENGTH_FIELD_NUMBER = 3;
+  private long keyLength_ = 0L;
+  /**
+   * <code>uint64 keyLength = 3 [json_name = "keyLength", (.buf.validate.field) = { ... }</code>
+   * @return The keyLength.
+   */
+  @java.lang.Override
+  public long getKeyLength() {
+    return keyLength_;
+  }
+
+  public static final int KEYHASH_FIELD_NUMBER = 4;
+  @SuppressWarnings("serial")
+  private volatile java.lang.Object keyHash_ = "";
+  /**
+   * <code>string keyHash = 4 [json_name = "keyHash", (.buf.validate.field) = { ... }</code>
+   * @return The keyHash.
+   */
+  @java.lang.Override
+  public java.lang.String getKeyHash() {
+    java.lang.Object ref = keyHash_;
+    if (ref instanceof java.lang.String) {
+      return (java.lang.String) ref;
+    } else {
+      com.google.protobuf.ByteString bs = 
+          (com.google.protobuf.ByteString) ref;
+      java.lang.String s = bs.toStringUtf8();
+      keyHash_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string keyHash = 4 [json_name = "keyHash", (.buf.validate.field) = { ... }</code>
+   * @return The bytes for keyHash.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getKeyHashBytes() {
+    java.lang.Object ref = keyHash_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      keyHash_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  public static final int METADATA_FIELD_NUMBER = 5;
+  private com.quipsec.v1.Metadata metadata_;
+  /**
+   * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+   * @return Whether the metadata field is set.
+   */
+  @java.lang.Override
+  public boolean hasMetadata() {
+    return ((bitField0_ & 0x00000001) != 0);
+  }
+  /**
+   * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+   * @return The metadata.
+   */
+  @java.lang.Override
+  public com.quipsec.v1.Metadata getMetadata() {
+    return metadata_ == null ? com.quipsec.v1.Metadata.getDefaultInstance() : metadata_;
+  }
+  /**
+   * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+   */
+  @java.lang.Override
+  public com.quipsec.v1.MetadataOrBuilder getMetadataOrBuilder() {
+    return metadata_ == null ? com.quipsec.v1.Metadata.getDefaultInstance() : metadata_;
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(keyId_)) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 1, keyId_);
+    }
+    if (!keys_.isEmpty()) {
+      output.writeBytes(2, keys_);
+    }
+    if (keyLength_ != 0L) {
+      output.writeUInt64(3, keyLength_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(keyHash_)) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 4, keyHash_);
+    }
+    if (((bitField0_ & 0x00000001) != 0)) {
+      output.writeMessage(5, getMetadata());
+    }
+    getUnknownFields().writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(keyId_)) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, keyId_);
+    }
+    if (!keys_.isEmpty()) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeBytesSize(2, keys_);
+    }
+    if (keyLength_ != 0L) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeUInt64Size(3, keyLength_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(keyHash_)) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(4, keyHash_);
+    }
+    if (((bitField0_ & 0x00000001) != 0)) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeMessageSize(5, getMetadata());
+    }
+    size += getUnknownFields().getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.quipsec.v1.KeyBulk)) {
+      return super.equals(obj);
+    }
+    com.quipsec.v1.KeyBulk other = (com.quipsec.v1.KeyBulk) obj;
+
+    if (!getKeyId()
+        .equals(other.getKeyId())) return false;
+    if (!getKeys()
+        .equals(other.getKeys())) return false;
+    if (getKeyLength()
+        != other.getKeyLength()) return false;
+    if (!getKeyHash()
+        .equals(other.getKeyHash())) return false;
+    if (hasMetadata() != other.hasMetadata()) return false;
+    if (hasMetadata()) {
+      if (!getMetadata()
+          .equals(other.getMetadata())) return false;
+    }
+    if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    hash = (37 * hash) + KEYID_FIELD_NUMBER;
+    hash = (53 * hash) + getKeyId().hashCode();
+    hash = (37 * hash) + KEYS_FIELD_NUMBER;
+    hash = (53 * hash) + getKeys().hashCode();
+    hash = (37 * hash) + KEYLENGTH_FIELD_NUMBER;
+    hash = (53 * hash) + com.google.protobuf.Internal.hashLong(
+        getKeyLength());
+    hash = (37 * hash) + KEYHASH_FIELD_NUMBER;
+    hash = (53 * hash) + getKeyHash().hashCode();
+    if (hasMetadata()) {
+      hash = (37 * hash) + METADATA_FIELD_NUMBER;
+      hash = (53 * hash) + getMetadata().hashCode();
+    }
+    hash = (29 * hash) + getUnknownFields().hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.quipsec.v1.KeyBulk parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.KeyBulk parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.KeyBulk parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.KeyBulk parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.KeyBulk parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.KeyBulk parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.KeyBulk parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.KeyBulk parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  public static com.quipsec.v1.KeyBulk parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+
+  public static com.quipsec.v1.KeyBulk parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.quipsec.v1.KeyBulk parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.KeyBulk parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.quipsec.v1.KeyBulk prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code quipsec.v1.KeyBulk}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:quipsec.v1.KeyBulk)
+      com.quipsec.v1.KeyBulkOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_KeyBulk_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_KeyBulk_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.quipsec.v1.KeyBulk.class, com.quipsec.v1.KeyBulk.Builder.class);
+    }
+
+    // Construct using com.quipsec.v1.KeyBulk.newBuilder()
+    private Builder() {
+      maybeForceBuilderInitialization();
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+      maybeForceBuilderInitialization();
+    }
+    private void maybeForceBuilderInitialization() {
+      if (com.google.protobuf.GeneratedMessageV3
+              .alwaysUseFieldBuilders) {
+        getMetadataFieldBuilder();
+      }
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      bitField0_ = 0;
+      keyId_ = "";
+      keys_ = com.google.protobuf.ByteString.EMPTY;
+      keyLength_ = 0L;
+      keyHash_ = "";
+      metadata_ = null;
+      if (metadataBuilder_ != null) {
+        metadataBuilder_.dispose();
+        metadataBuilder_ = null;
+      }
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_KeyBulk_descriptor;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.KeyBulk getDefaultInstanceForType() {
+      return com.quipsec.v1.KeyBulk.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.KeyBulk build() {
+      com.quipsec.v1.KeyBulk result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.KeyBulk buildPartial() {
+      com.quipsec.v1.KeyBulk result = new com.quipsec.v1.KeyBulk(this);
+      if (bitField0_ != 0) { buildPartial0(result); }
+      onBuilt();
+      return result;
+    }
+
+    private void buildPartial0(com.quipsec.v1.KeyBulk result) {
+      int from_bitField0_ = bitField0_;
+      if (((from_bitField0_ & 0x00000001) != 0)) {
+        result.keyId_ = keyId_;
+      }
+      if (((from_bitField0_ & 0x00000002) != 0)) {
+        result.keys_ = keys_;
+      }
+      if (((from_bitField0_ & 0x00000004) != 0)) {
+        result.keyLength_ = keyLength_;
+      }
+      if (((from_bitField0_ & 0x00000008) != 0)) {
+        result.keyHash_ = keyHash_;
+      }
+      int to_bitField0_ = 0;
+      if (((from_bitField0_ & 0x00000010) != 0)) {
+        result.metadata_ = metadataBuilder_ == null
+            ? metadata_
+            : metadataBuilder_.build();
+        to_bitField0_ |= 0x00000001;
+      }
+      result.bitField0_ |= to_bitField0_;
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.quipsec.v1.KeyBulk) {
+        return mergeFrom((com.quipsec.v1.KeyBulk)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.quipsec.v1.KeyBulk other) {
+      if (other == com.quipsec.v1.KeyBulk.getDefaultInstance()) return this;
+      if (!other.getKeyId().isEmpty()) {
+        keyId_ = other.keyId_;
+        bitField0_ |= 0x00000001;
+        onChanged();
+      }
+      if (other.getKeys() != com.google.protobuf.ByteString.EMPTY) {
+        setKeys(other.getKeys());
+      }
+      if (other.getKeyLength() != 0L) {
+        setKeyLength(other.getKeyLength());
+      }
+      if (!other.getKeyHash().isEmpty()) {
+        keyHash_ = other.keyHash_;
+        bitField0_ |= 0x00000008;
+        onChanged();
+      }
+      if (other.hasMetadata()) {
+        mergeMetadata(other.getMetadata());
+      }
+      this.mergeUnknownFields(other.getUnknownFields());
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            case 10: {
+              keyId_ = input.readStringRequireUtf8();
+              bitField0_ |= 0x00000001;
+              break;
+            } // case 10
+            case 18: {
+              keys_ = input.readBytes();
+              bitField0_ |= 0x00000002;
+              break;
+            } // case 18
+            case 24: {
+              keyLength_ = input.readUInt64();
+              bitField0_ |= 0x00000004;
+              break;
+            } // case 24
+            case 34: {
+              keyHash_ = input.readStringRequireUtf8();
+              bitField0_ |= 0x00000008;
+              break;
+            } // case 34
+            case 42: {
+              input.readMessage(
+                  getMetadataFieldBuilder().getBuilder(),
+                  extensionRegistry);
+              bitField0_ |= 0x00000010;
+              break;
+            } // case 42
+            default: {
+              if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                done = true; // was an endgroup tag
+              }
+              break;
+            } // default:
+          } // switch (tag)
+        } // while (!done)
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.unwrapIOException();
+      } finally {
+        onChanged();
+      } // finally
+      return this;
+    }
+    private int bitField0_;
+
+    private java.lang.Object keyId_ = "";
+    /**
+     * <code>string keyId = 1 [json_name = "keyId", (.buf.validate.field) = { ... }</code>
+     * @return The keyId.
+     */
+    public java.lang.String getKeyId() {
+      java.lang.Object ref = keyId_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        keyId_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string keyId = 1 [json_name = "keyId", (.buf.validate.field) = { ... }</code>
+     * @return The bytes for keyId.
+     */
+    public com.google.protobuf.ByteString
+        getKeyIdBytes() {
+      java.lang.Object ref = keyId_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        keyId_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string keyId = 1 [json_name = "keyId", (.buf.validate.field) = { ... }</code>
+     * @param value The keyId to set.
+     * @return This builder for chaining.
+     */
+    public Builder setKeyId(
+        java.lang.String value) {
+      if (value == null) { throw new NullPointerException(); }
+      keyId_ = value;
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string keyId = 1 [json_name = "keyId", (.buf.validate.field) = { ... }</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearKeyId() {
+      keyId_ = getDefaultInstance().getKeyId();
+      bitField0_ = (bitField0_ & ~0x00000001);
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string keyId = 1 [json_name = "keyId", (.buf.validate.field) = { ... }</code>
+     * @param value The bytes for keyId to set.
+     * @return This builder for chaining.
+     */
+    public Builder setKeyIdBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) { throw new NullPointerException(); }
+      checkByteStringIsUtf8(value);
+      keyId_ = value;
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+
+    private com.google.protobuf.ByteString keys_ = com.google.protobuf.ByteString.EMPTY;
+    /**
+     * <code>bytes keys = 2 [json_name = "keys", (.buf.validate.field) = { ... }</code>
+     * @return The keys.
+     */
+    @java.lang.Override
+    public com.google.protobuf.ByteString getKeys() {
+      return keys_;
+    }
+    /**
+     * <code>bytes keys = 2 [json_name = "keys", (.buf.validate.field) = { ... }</code>
+     * @param value The keys to set.
+     * @return This builder for chaining.
+     */
+    public Builder setKeys(com.google.protobuf.ByteString value) {
+      if (value == null) { throw new NullPointerException(); }
+      keys_ = value;
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>bytes keys = 2 [json_name = "keys", (.buf.validate.field) = { ... }</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearKeys() {
+      bitField0_ = (bitField0_ & ~0x00000002);
+      keys_ = getDefaultInstance().getKeys();
+      onChanged();
+      return this;
+    }
+
+    private long keyLength_ ;
+    /**
+     * <code>uint64 keyLength = 3 [json_name = "keyLength", (.buf.validate.field) = { ... }</code>
+     * @return The keyLength.
+     */
+    @java.lang.Override
+    public long getKeyLength() {
+      return keyLength_;
+    }
+    /**
+     * <code>uint64 keyLength = 3 [json_name = "keyLength", (.buf.validate.field) = { ... }</code>
+     * @param value The keyLength to set.
+     * @return This builder for chaining.
+     */
+    public Builder setKeyLength(long value) {
+
+      keyLength_ = value;
+      bitField0_ |= 0x00000004;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>uint64 keyLength = 3 [json_name = "keyLength", (.buf.validate.field) = { ... }</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearKeyLength() {
+      bitField0_ = (bitField0_ & ~0x00000004);
+      keyLength_ = 0L;
+      onChanged();
+      return this;
+    }
+
+    private java.lang.Object keyHash_ = "";
+    /**
+     * <code>string keyHash = 4 [json_name = "keyHash", (.buf.validate.field) = { ... }</code>
+     * @return The keyHash.
+     */
+    public java.lang.String getKeyHash() {
+      java.lang.Object ref = keyHash_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        keyHash_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string keyHash = 4 [json_name = "keyHash", (.buf.validate.field) = { ... }</code>
+     * @return The bytes for keyHash.
+     */
+    public com.google.protobuf.ByteString
+        getKeyHashBytes() {
+      java.lang.Object ref = keyHash_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        keyHash_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string keyHash = 4 [json_name = "keyHash", (.buf.validate.field) = { ... }</code>
+     * @param value The keyHash to set.
+     * @return This builder for chaining.
+     */
+    public Builder setKeyHash(
+        java.lang.String value) {
+      if (value == null) { throw new NullPointerException(); }
+      keyHash_ = value;
+      bitField0_ |= 0x00000008;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string keyHash = 4 [json_name = "keyHash", (.buf.validate.field) = { ... }</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearKeyHash() {
+      keyHash_ = getDefaultInstance().getKeyHash();
+      bitField0_ = (bitField0_ & ~0x00000008);
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string keyHash = 4 [json_name = "keyHash", (.buf.validate.field) = { ... }</code>
+     * @param value The bytes for keyHash to set.
+     * @return This builder for chaining.
+     */
+    public Builder setKeyHashBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) { throw new NullPointerException(); }
+      checkByteStringIsUtf8(value);
+      keyHash_ = value;
+      bitField0_ |= 0x00000008;
+      onChanged();
+      return this;
+    }
+
+    private com.quipsec.v1.Metadata metadata_;
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.quipsec.v1.Metadata, com.quipsec.v1.Metadata.Builder, com.quipsec.v1.MetadataOrBuilder> metadataBuilder_;
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     * @return Whether the metadata field is set.
+     */
+    public boolean hasMetadata() {
+      return ((bitField0_ & 0x00000010) != 0);
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     * @return The metadata.
+     */
+    public com.quipsec.v1.Metadata getMetadata() {
+      if (metadataBuilder_ == null) {
+        return metadata_ == null ? com.quipsec.v1.Metadata.getDefaultInstance() : metadata_;
+      } else {
+        return metadataBuilder_.getMessage();
+      }
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    public Builder setMetadata(com.quipsec.v1.Metadata value) {
+      if (metadataBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        metadata_ = value;
+      } else {
+        metadataBuilder_.setMessage(value);
+      }
+      bitField0_ |= 0x00000010;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    public Builder setMetadata(
+        com.quipsec.v1.Metadata.Builder builderForValue) {
+      if (metadataBuilder_ == null) {
+        metadata_ = builderForValue.build();
+      } else {
+        metadataBuilder_.setMessage(builderForValue.build());
+      }
+      bitField0_ |= 0x00000010;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    public Builder mergeMetadata(com.quipsec.v1.Metadata value) {
+      if (metadataBuilder_ == null) {
+        if (((bitField0_ & 0x00000010) != 0) &&
+          metadata_ != null &&
+          metadata_ != com.quipsec.v1.Metadata.getDefaultInstance()) {
+          getMetadataBuilder().mergeFrom(value);
+        } else {
+          metadata_ = value;
+        }
+      } else {
+        metadataBuilder_.mergeFrom(value);
+      }
+      if (metadata_ != null) {
+        bitField0_ |= 0x00000010;
+        onChanged();
+      }
+      return this;
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    public Builder clearMetadata() {
+      bitField0_ = (bitField0_ & ~0x00000010);
+      metadata_ = null;
+      if (metadataBuilder_ != null) {
+        metadataBuilder_.dispose();
+        metadataBuilder_ = null;
+      }
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    public com.quipsec.v1.Metadata.Builder getMetadataBuilder() {
+      bitField0_ |= 0x00000010;
+      onChanged();
+      return getMetadataFieldBuilder().getBuilder();
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    public com.quipsec.v1.MetadataOrBuilder getMetadataOrBuilder() {
+      if (metadataBuilder_ != null) {
+        return metadataBuilder_.getMessageOrBuilder();
+      } else {
+        return metadata_ == null ?
+            com.quipsec.v1.Metadata.getDefaultInstance() : metadata_;
+      }
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.quipsec.v1.Metadata, com.quipsec.v1.Metadata.Builder, com.quipsec.v1.MetadataOrBuilder> 
+        getMetadataFieldBuilder() {
+      if (metadataBuilder_ == null) {
+        metadataBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+            com.quipsec.v1.Metadata, com.quipsec.v1.Metadata.Builder, com.quipsec.v1.MetadataOrBuilder>(
+                getMetadata(),
+                getParentForChildren(),
+                isClean());
+        metadata_ = null;
+      }
+      return metadataBuilder_;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:quipsec.v1.KeyBulk)
+  }
+
+  // @@protoc_insertion_point(class_scope:quipsec.v1.KeyBulk)
+  private static final com.quipsec.v1.KeyBulk DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.quipsec.v1.KeyBulk();
+  }
+
+  public static com.quipsec.v1.KeyBulk getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<KeyBulk>
+      PARSER = new com.google.protobuf.AbstractParser<KeyBulk>() {
+    @java.lang.Override
+    public KeyBulk parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      Builder builder = newBuilder();
+      try {
+        builder.mergeFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(builder.buildPartial());
+      } catch (com.google.protobuf.UninitializedMessageException e) {
+        throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(e)
+            .setUnfinishedMessage(builder.buildPartial());
+      }
+      return builder.buildPartial();
+    }
+  };
+
+  public static com.google.protobuf.Parser<KeyBulk> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<KeyBulk> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.quipsec.v1.KeyBulk getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+
diff --git a/gen/java/com/quipsec/v1/KeyBulkOrBuilder.java b/gen/java/com/quipsec/v1/KeyBulkOrBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e771c6009556eaecaee356210493e8f23a9ce6c
--- /dev/null
+++ b/gen/java/com/quipsec/v1/KeyBulkOrBuilder.java
@@ -0,0 +1,61 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+public interface KeyBulkOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:quipsec.v1.KeyBulk)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>string keyId = 1 [json_name = "keyId", (.buf.validate.field) = { ... }</code>
+   * @return The keyId.
+   */
+  java.lang.String getKeyId();
+  /**
+   * <code>string keyId = 1 [json_name = "keyId", (.buf.validate.field) = { ... }</code>
+   * @return The bytes for keyId.
+   */
+  com.google.protobuf.ByteString
+      getKeyIdBytes();
+
+  /**
+   * <code>bytes keys = 2 [json_name = "keys", (.buf.validate.field) = { ... }</code>
+   * @return The keys.
+   */
+  com.google.protobuf.ByteString getKeys();
+
+  /**
+   * <code>uint64 keyLength = 3 [json_name = "keyLength", (.buf.validate.field) = { ... }</code>
+   * @return The keyLength.
+   */
+  long getKeyLength();
+
+  /**
+   * <code>string keyHash = 4 [json_name = "keyHash", (.buf.validate.field) = { ... }</code>
+   * @return The keyHash.
+   */
+  java.lang.String getKeyHash();
+  /**
+   * <code>string keyHash = 4 [json_name = "keyHash", (.buf.validate.field) = { ... }</code>
+   * @return The bytes for keyHash.
+   */
+  com.google.protobuf.ByteString
+      getKeyHashBytes();
+
+  /**
+   * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+   * @return Whether the metadata field is set.
+   */
+  boolean hasMetadata();
+  /**
+   * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+   * @return The metadata.
+   */
+  com.quipsec.v1.Metadata getMetadata();
+  /**
+   * <code>.quipsec.v1.Metadata metadata = 5 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+   */
+  com.quipsec.v1.MetadataOrBuilder getMetadataOrBuilder();
+}
diff --git a/gen/java/com/quipsec/v1/KmsQkdmCommunicationServiceGrpc.java b/gen/java/com/quipsec/v1/KmsQkdmCommunicationServiceGrpc.java
new file mode 100644
index 0000000000000000000000000000000000000000..a384b5cf4920aed9ce9a30e5642cfab694dd1f44
--- /dev/null
+++ b/gen/java/com/quipsec/v1/KmsQkdmCommunicationServiceGrpc.java
@@ -0,0 +1,567 @@
+package com.quipsec.v1;
+
+import static io.grpc.MethodDescriptor.generateFullMethodName;
+
+/**
+ */
+@javax.annotation.Generated(
+    value = "by gRPC proto compiler (version 1.59.0)",
+    comments = "Source: quicsep/quicsep.proto")
+@io.grpc.stub.annotations.GrpcGenerated
+public final class KmsQkdmCommunicationServiceGrpc {
+
+  private KmsQkdmCommunicationServiceGrpc() {}
+
+  public static final java.lang.String SERVICE_NAME = "quipsec.v1.KmsQkdmCommunicationService";
+
+  // Static method descriptors that strictly reflect the proto.
+  private static volatile io.grpc.MethodDescriptor<com.quipsec.v1.CapabilitiesRequest,
+      com.quipsec.v1.CapabilitiesResponse> getCapabilitiesMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "Capabilities",
+      requestType = com.quipsec.v1.CapabilitiesRequest.class,
+      responseType = com.quipsec.v1.CapabilitiesResponse.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<com.quipsec.v1.CapabilitiesRequest,
+      com.quipsec.v1.CapabilitiesResponse> getCapabilitiesMethod() {
+    io.grpc.MethodDescriptor<com.quipsec.v1.CapabilitiesRequest, com.quipsec.v1.CapabilitiesResponse> getCapabilitiesMethod;
+    if ((getCapabilitiesMethod = KmsQkdmCommunicationServiceGrpc.getCapabilitiesMethod) == null) {
+      synchronized (KmsQkdmCommunicationServiceGrpc.class) {
+        if ((getCapabilitiesMethod = KmsQkdmCommunicationServiceGrpc.getCapabilitiesMethod) == null) {
+          KmsQkdmCommunicationServiceGrpc.getCapabilitiesMethod = getCapabilitiesMethod =
+              io.grpc.MethodDescriptor.<com.quipsec.v1.CapabilitiesRequest, com.quipsec.v1.CapabilitiesResponse>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "Capabilities"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.quipsec.v1.CapabilitiesRequest.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.quipsec.v1.CapabilitiesResponse.getDefaultInstance()))
+              .setSchemaDescriptor(new KmsQkdmCommunicationServiceMethodDescriptorSupplier("Capabilities"))
+              .build();
+        }
+      }
+    }
+    return getCapabilitiesMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<com.quipsec.v1.PushKeysRequest,
+      com.quipsec.v1.PushKeysResponse> getPushKeysMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "PushKeys",
+      requestType = com.quipsec.v1.PushKeysRequest.class,
+      responseType = com.quipsec.v1.PushKeysResponse.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<com.quipsec.v1.PushKeysRequest,
+      com.quipsec.v1.PushKeysResponse> getPushKeysMethod() {
+    io.grpc.MethodDescriptor<com.quipsec.v1.PushKeysRequest, com.quipsec.v1.PushKeysResponse> getPushKeysMethod;
+    if ((getPushKeysMethod = KmsQkdmCommunicationServiceGrpc.getPushKeysMethod) == null) {
+      synchronized (KmsQkdmCommunicationServiceGrpc.class) {
+        if ((getPushKeysMethod = KmsQkdmCommunicationServiceGrpc.getPushKeysMethod) == null) {
+          KmsQkdmCommunicationServiceGrpc.getPushKeysMethod = getPushKeysMethod =
+              io.grpc.MethodDescriptor.<com.quipsec.v1.PushKeysRequest, com.quipsec.v1.PushKeysResponse>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "PushKeys"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.quipsec.v1.PushKeysRequest.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.quipsec.v1.PushKeysResponse.getDefaultInstance()))
+              .setSchemaDescriptor(new KmsQkdmCommunicationServiceMethodDescriptorSupplier("PushKeys"))
+              .build();
+        }
+      }
+    }
+    return getPushKeysMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<com.quipsec.v1.QkdmMetadataRequest,
+      com.quipsec.v1.QkdmMetadataResponse> getQkdmMetadataMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "QkdmMetadata",
+      requestType = com.quipsec.v1.QkdmMetadataRequest.class,
+      responseType = com.quipsec.v1.QkdmMetadataResponse.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<com.quipsec.v1.QkdmMetadataRequest,
+      com.quipsec.v1.QkdmMetadataResponse> getQkdmMetadataMethod() {
+    io.grpc.MethodDescriptor<com.quipsec.v1.QkdmMetadataRequest, com.quipsec.v1.QkdmMetadataResponse> getQkdmMetadataMethod;
+    if ((getQkdmMetadataMethod = KmsQkdmCommunicationServiceGrpc.getQkdmMetadataMethod) == null) {
+      synchronized (KmsQkdmCommunicationServiceGrpc.class) {
+        if ((getQkdmMetadataMethod = KmsQkdmCommunicationServiceGrpc.getQkdmMetadataMethod) == null) {
+          KmsQkdmCommunicationServiceGrpc.getQkdmMetadataMethod = getQkdmMetadataMethod =
+              io.grpc.MethodDescriptor.<com.quipsec.v1.QkdmMetadataRequest, com.quipsec.v1.QkdmMetadataResponse>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "QkdmMetadata"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.quipsec.v1.QkdmMetadataRequest.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.quipsec.v1.QkdmMetadataResponse.getDefaultInstance()))
+              .setSchemaDescriptor(new KmsQkdmCommunicationServiceMethodDescriptorSupplier("QkdmMetadata"))
+              .build();
+        }
+      }
+    }
+    return getQkdmMetadataMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<com.quipsec.v1.ShutdownNotificationRequest,
+      com.quipsec.v1.ShutdownNotificationResponse> getShutdownNotificationMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "ShutdownNotification",
+      requestType = com.quipsec.v1.ShutdownNotificationRequest.class,
+      responseType = com.quipsec.v1.ShutdownNotificationResponse.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<com.quipsec.v1.ShutdownNotificationRequest,
+      com.quipsec.v1.ShutdownNotificationResponse> getShutdownNotificationMethod() {
+    io.grpc.MethodDescriptor<com.quipsec.v1.ShutdownNotificationRequest, com.quipsec.v1.ShutdownNotificationResponse> getShutdownNotificationMethod;
+    if ((getShutdownNotificationMethod = KmsQkdmCommunicationServiceGrpc.getShutdownNotificationMethod) == null) {
+      synchronized (KmsQkdmCommunicationServiceGrpc.class) {
+        if ((getShutdownNotificationMethod = KmsQkdmCommunicationServiceGrpc.getShutdownNotificationMethod) == null) {
+          KmsQkdmCommunicationServiceGrpc.getShutdownNotificationMethod = getShutdownNotificationMethod =
+              io.grpc.MethodDescriptor.<com.quipsec.v1.ShutdownNotificationRequest, com.quipsec.v1.ShutdownNotificationResponse>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "ShutdownNotification"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.quipsec.v1.ShutdownNotificationRequest.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.quipsec.v1.ShutdownNotificationResponse.getDefaultInstance()))
+              .setSchemaDescriptor(new KmsQkdmCommunicationServiceMethodDescriptorSupplier("ShutdownNotification"))
+              .build();
+        }
+      }
+    }
+    return getShutdownNotificationMethod;
+  }
+
+  /**
+   * Creates a new async stub that supports all call types for the service
+   */
+  public static KmsQkdmCommunicationServiceStub newStub(io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<KmsQkdmCommunicationServiceStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<KmsQkdmCommunicationServiceStub>() {
+        @java.lang.Override
+        public KmsQkdmCommunicationServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new KmsQkdmCommunicationServiceStub(channel, callOptions);
+        }
+      };
+    return KmsQkdmCommunicationServiceStub.newStub(factory, channel);
+  }
+
+  /**
+   * Creates a new blocking-style stub that supports unary and streaming output calls on the service
+   */
+  public static KmsQkdmCommunicationServiceBlockingStub newBlockingStub(
+      io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<KmsQkdmCommunicationServiceBlockingStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<KmsQkdmCommunicationServiceBlockingStub>() {
+        @java.lang.Override
+        public KmsQkdmCommunicationServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new KmsQkdmCommunicationServiceBlockingStub(channel, callOptions);
+        }
+      };
+    return KmsQkdmCommunicationServiceBlockingStub.newStub(factory, channel);
+  }
+
+  /**
+   * Creates a new ListenableFuture-style stub that supports unary calls on the service
+   */
+  public static KmsQkdmCommunicationServiceFutureStub newFutureStub(
+      io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<KmsQkdmCommunicationServiceFutureStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<KmsQkdmCommunicationServiceFutureStub>() {
+        @java.lang.Override
+        public KmsQkdmCommunicationServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new KmsQkdmCommunicationServiceFutureStub(channel, callOptions);
+        }
+      };
+    return KmsQkdmCommunicationServiceFutureStub.newStub(factory, channel);
+  }
+
+  /**
+   */
+  public interface AsyncService {
+
+    /**
+     * <pre>
+     * Initial information about the QKDM.
+     * </pre>
+     */
+    default void capabilities(com.quipsec.v1.CapabilitiesRequest request,
+        io.grpc.stub.StreamObserver<com.quipsec.v1.CapabilitiesResponse> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getCapabilitiesMethod(), responseObserver);
+    }
+
+    /**
+     * <pre>
+     * NOTE: A stream could also be considered
+     * Push of keys from the QKDM to KMS in irregular intervals.
+     * </pre>
+     */
+    default void pushKeys(com.quipsec.v1.PushKeysRequest request,
+        io.grpc.stub.StreamObserver<com.quipsec.v1.PushKeysResponse> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getPushKeysMethod(), responseObserver);
+    }
+
+    /**
+     * <pre>
+     * Statistics and information about the QKDM.
+     * </pre>
+     */
+    default void qkdmMetadata(com.quipsec.v1.QkdmMetadataRequest request,
+        io.grpc.stub.StreamObserver<com.quipsec.v1.QkdmMetadataResponse> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getQkdmMetadataMethod(), responseObserver);
+    }
+
+    /**
+     * <pre>
+     * Notification for the KMS on QKDM shutdown.
+     * </pre>
+     */
+    default void shutdownNotification(com.quipsec.v1.ShutdownNotificationRequest request,
+        io.grpc.stub.StreamObserver<com.quipsec.v1.ShutdownNotificationResponse> responseObserver) {
+      io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getShutdownNotificationMethod(), responseObserver);
+    }
+  }
+
+  /**
+   * Base class for the server implementation of the service KmsQkdmCommunicationService.
+   */
+  public static abstract class KmsQkdmCommunicationServiceImplBase
+      implements io.grpc.BindableService, AsyncService {
+
+    @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
+      return KmsQkdmCommunicationServiceGrpc.bindService(this);
+    }
+  }
+
+  /**
+   * A stub to allow clients to do asynchronous rpc calls to service KmsQkdmCommunicationService.
+   */
+  public static final class KmsQkdmCommunicationServiceStub
+      extends io.grpc.stub.AbstractAsyncStub<KmsQkdmCommunicationServiceStub> {
+    private KmsQkdmCommunicationServiceStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected KmsQkdmCommunicationServiceStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new KmsQkdmCommunicationServiceStub(channel, callOptions);
+    }
+
+    /**
+     * <pre>
+     * Initial information about the QKDM.
+     * </pre>
+     */
+    public void capabilities(com.quipsec.v1.CapabilitiesRequest request,
+        io.grpc.stub.StreamObserver<com.quipsec.v1.CapabilitiesResponse> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getCapabilitiesMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     * <pre>
+     * NOTE: A stream could also be considered
+     * Push of keys from the QKDM to KMS in irregular intervals.
+     * </pre>
+     */
+    public void pushKeys(com.quipsec.v1.PushKeysRequest request,
+        io.grpc.stub.StreamObserver<com.quipsec.v1.PushKeysResponse> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getPushKeysMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     * <pre>
+     * Statistics and information about the QKDM.
+     * </pre>
+     */
+    public void qkdmMetadata(com.quipsec.v1.QkdmMetadataRequest request,
+        io.grpc.stub.StreamObserver<com.quipsec.v1.QkdmMetadataResponse> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getQkdmMetadataMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     * <pre>
+     * Notification for the KMS on QKDM shutdown.
+     * </pre>
+     */
+    public void shutdownNotification(com.quipsec.v1.ShutdownNotificationRequest request,
+        io.grpc.stub.StreamObserver<com.quipsec.v1.ShutdownNotificationResponse> responseObserver) {
+      io.grpc.stub.ClientCalls.asyncUnaryCall(
+          getChannel().newCall(getShutdownNotificationMethod(), getCallOptions()), request, responseObserver);
+    }
+  }
+
+  /**
+   * A stub to allow clients to do synchronous rpc calls to service KmsQkdmCommunicationService.
+   */
+  public static final class KmsQkdmCommunicationServiceBlockingStub
+      extends io.grpc.stub.AbstractBlockingStub<KmsQkdmCommunicationServiceBlockingStub> {
+    private KmsQkdmCommunicationServiceBlockingStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected KmsQkdmCommunicationServiceBlockingStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new KmsQkdmCommunicationServiceBlockingStub(channel, callOptions);
+    }
+
+    /**
+     * <pre>
+     * Initial information about the QKDM.
+     * </pre>
+     */
+    public com.quipsec.v1.CapabilitiesResponse capabilities(com.quipsec.v1.CapabilitiesRequest request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getCapabilitiesMethod(), getCallOptions(), request);
+    }
+
+    /**
+     * <pre>
+     * NOTE: A stream could also be considered
+     * Push of keys from the QKDM to KMS in irregular intervals.
+     * </pre>
+     */
+    public com.quipsec.v1.PushKeysResponse pushKeys(com.quipsec.v1.PushKeysRequest request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getPushKeysMethod(), getCallOptions(), request);
+    }
+
+    /**
+     * <pre>
+     * Statistics and information about the QKDM.
+     * </pre>
+     */
+    public com.quipsec.v1.QkdmMetadataResponse qkdmMetadata(com.quipsec.v1.QkdmMetadataRequest request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getQkdmMetadataMethod(), getCallOptions(), request);
+    }
+
+    /**
+     * <pre>
+     * Notification for the KMS on QKDM shutdown.
+     * </pre>
+     */
+    public com.quipsec.v1.ShutdownNotificationResponse shutdownNotification(com.quipsec.v1.ShutdownNotificationRequest request) {
+      return io.grpc.stub.ClientCalls.blockingUnaryCall(
+          getChannel(), getShutdownNotificationMethod(), getCallOptions(), request);
+    }
+  }
+
+  /**
+   * A stub to allow clients to do ListenableFuture-style rpc calls to service KmsQkdmCommunicationService.
+   */
+  public static final class KmsQkdmCommunicationServiceFutureStub
+      extends io.grpc.stub.AbstractFutureStub<KmsQkdmCommunicationServiceFutureStub> {
+    private KmsQkdmCommunicationServiceFutureStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected KmsQkdmCommunicationServiceFutureStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new KmsQkdmCommunicationServiceFutureStub(channel, callOptions);
+    }
+
+    /**
+     * <pre>
+     * Initial information about the QKDM.
+     * </pre>
+     */
+    public com.google.common.util.concurrent.ListenableFuture<com.quipsec.v1.CapabilitiesResponse> capabilities(
+        com.quipsec.v1.CapabilitiesRequest request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getCapabilitiesMethod(), getCallOptions()), request);
+    }
+
+    /**
+     * <pre>
+     * NOTE: A stream could also be considered
+     * Push of keys from the QKDM to KMS in irregular intervals.
+     * </pre>
+     */
+    public com.google.common.util.concurrent.ListenableFuture<com.quipsec.v1.PushKeysResponse> pushKeys(
+        com.quipsec.v1.PushKeysRequest request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getPushKeysMethod(), getCallOptions()), request);
+    }
+
+    /**
+     * <pre>
+     * Statistics and information about the QKDM.
+     * </pre>
+     */
+    public com.google.common.util.concurrent.ListenableFuture<com.quipsec.v1.QkdmMetadataResponse> qkdmMetadata(
+        com.quipsec.v1.QkdmMetadataRequest request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getQkdmMetadataMethod(), getCallOptions()), request);
+    }
+
+    /**
+     * <pre>
+     * Notification for the KMS on QKDM shutdown.
+     * </pre>
+     */
+    public com.google.common.util.concurrent.ListenableFuture<com.quipsec.v1.ShutdownNotificationResponse> shutdownNotification(
+        com.quipsec.v1.ShutdownNotificationRequest request) {
+      return io.grpc.stub.ClientCalls.futureUnaryCall(
+          getChannel().newCall(getShutdownNotificationMethod(), getCallOptions()), request);
+    }
+  }
+
+  private static final int METHODID_CAPABILITIES = 0;
+  private static final int METHODID_PUSH_KEYS = 1;
+  private static final int METHODID_QKDM_METADATA = 2;
+  private static final int METHODID_SHUTDOWN_NOTIFICATION = 3;
+
+  private static final class MethodHandlers<Req, Resp> implements
+      io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {
+    private final AsyncService serviceImpl;
+    private final int methodId;
+
+    MethodHandlers(AsyncService serviceImpl, int methodId) {
+      this.serviceImpl = serviceImpl;
+      this.methodId = methodId;
+    }
+
+    @java.lang.Override
+    @java.lang.SuppressWarnings("unchecked")
+    public void invoke(Req request, io.grpc.stub.StreamObserver<Resp> responseObserver) {
+      switch (methodId) {
+        case METHODID_CAPABILITIES:
+          serviceImpl.capabilities((com.quipsec.v1.CapabilitiesRequest) request,
+              (io.grpc.stub.StreamObserver<com.quipsec.v1.CapabilitiesResponse>) responseObserver);
+          break;
+        case METHODID_PUSH_KEYS:
+          serviceImpl.pushKeys((com.quipsec.v1.PushKeysRequest) request,
+              (io.grpc.stub.StreamObserver<com.quipsec.v1.PushKeysResponse>) responseObserver);
+          break;
+        case METHODID_QKDM_METADATA:
+          serviceImpl.qkdmMetadata((com.quipsec.v1.QkdmMetadataRequest) request,
+              (io.grpc.stub.StreamObserver<com.quipsec.v1.QkdmMetadataResponse>) responseObserver);
+          break;
+        case METHODID_SHUTDOWN_NOTIFICATION:
+          serviceImpl.shutdownNotification((com.quipsec.v1.ShutdownNotificationRequest) request,
+              (io.grpc.stub.StreamObserver<com.quipsec.v1.ShutdownNotificationResponse>) responseObserver);
+          break;
+        default:
+          throw new AssertionError();
+      }
+    }
+
+    @java.lang.Override
+    @java.lang.SuppressWarnings("unchecked")
+    public io.grpc.stub.StreamObserver<Req> invoke(
+        io.grpc.stub.StreamObserver<Resp> responseObserver) {
+      switch (methodId) {
+        default:
+          throw new AssertionError();
+      }
+    }
+  }
+
+  public static final io.grpc.ServerServiceDefinition bindService(AsyncService service) {
+    return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
+        .addMethod(
+          getCapabilitiesMethod(),
+          io.grpc.stub.ServerCalls.asyncUnaryCall(
+            new MethodHandlers<
+              com.quipsec.v1.CapabilitiesRequest,
+              com.quipsec.v1.CapabilitiesResponse>(
+                service, METHODID_CAPABILITIES)))
+        .addMethod(
+          getPushKeysMethod(),
+          io.grpc.stub.ServerCalls.asyncUnaryCall(
+            new MethodHandlers<
+              com.quipsec.v1.PushKeysRequest,
+              com.quipsec.v1.PushKeysResponse>(
+                service, METHODID_PUSH_KEYS)))
+        .addMethod(
+          getQkdmMetadataMethod(),
+          io.grpc.stub.ServerCalls.asyncUnaryCall(
+            new MethodHandlers<
+              com.quipsec.v1.QkdmMetadataRequest,
+              com.quipsec.v1.QkdmMetadataResponse>(
+                service, METHODID_QKDM_METADATA)))
+        .addMethod(
+          getShutdownNotificationMethod(),
+          io.grpc.stub.ServerCalls.asyncUnaryCall(
+            new MethodHandlers<
+              com.quipsec.v1.ShutdownNotificationRequest,
+              com.quipsec.v1.ShutdownNotificationResponse>(
+                service, METHODID_SHUTDOWN_NOTIFICATION)))
+        .build();
+  }
+
+  private static abstract class KmsQkdmCommunicationServiceBaseDescriptorSupplier
+      implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier {
+    KmsQkdmCommunicationServiceBaseDescriptorSupplier() {}
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {
+      return com.quipsec.v1.QuicsepProto.getDescriptor();
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {
+      return getFileDescriptor().findServiceByName("KmsQkdmCommunicationService");
+    }
+  }
+
+  private static final class KmsQkdmCommunicationServiceFileDescriptorSupplier
+      extends KmsQkdmCommunicationServiceBaseDescriptorSupplier {
+    KmsQkdmCommunicationServiceFileDescriptorSupplier() {}
+  }
+
+  private static final class KmsQkdmCommunicationServiceMethodDescriptorSupplier
+      extends KmsQkdmCommunicationServiceBaseDescriptorSupplier
+      implements io.grpc.protobuf.ProtoMethodDescriptorSupplier {
+    private final java.lang.String methodName;
+
+    KmsQkdmCommunicationServiceMethodDescriptorSupplier(java.lang.String methodName) {
+      this.methodName = methodName;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {
+      return getServiceDescriptor().findMethodByName(methodName);
+    }
+  }
+
+  private static volatile io.grpc.ServiceDescriptor serviceDescriptor;
+
+  public static io.grpc.ServiceDescriptor getServiceDescriptor() {
+    io.grpc.ServiceDescriptor result = serviceDescriptor;
+    if (result == null) {
+      synchronized (KmsQkdmCommunicationServiceGrpc.class) {
+        result = serviceDescriptor;
+        if (result == null) {
+          serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME)
+              .setSchemaDescriptor(new KmsQkdmCommunicationServiceFileDescriptorSupplier())
+              .addMethod(getCapabilitiesMethod())
+              .addMethod(getPushKeysMethod())
+              .addMethod(getQkdmMetadataMethod())
+              .addMethod(getShutdownNotificationMethod())
+              .build();
+        }
+      }
+    }
+    return result;
+  }
+}
diff --git a/gen/java/com/quipsec/v1/Metadata.java b/gen/java/com/quipsec/v1/Metadata.java
new file mode 100644
index 0000000000000000000000000000000000000000..de3d201476456f68f5d1789be3cc0be4cba2c25d
--- /dev/null
+++ b/gen/java/com/quipsec/v1/Metadata.java
@@ -0,0 +1,1133 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+/**
+ * Protobuf type {@code quipsec.v1.Metadata}
+ */
+public final class Metadata extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:quipsec.v1.Metadata)
+    MetadataOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use Metadata.newBuilder() to construct.
+  private Metadata(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private Metadata() {
+    vendor_ = "";
+    version_ = "";
+    sourceId_ = "";
+    destinationId_ = "";
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new Metadata();
+  }
+
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_Metadata_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_Metadata_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.quipsec.v1.Metadata.class, com.quipsec.v1.Metadata.Builder.class);
+  }
+
+  public static final int TIMESTAMP_FIELD_NUMBER = 1;
+  private long timestamp_ = 0L;
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp"];</code>
+   * @return The timestamp.
+   */
+  @java.lang.Override
+  public long getTimestamp() {
+    return timestamp_;
+  }
+
+  public static final int VENDOR_FIELD_NUMBER = 2;
+  @SuppressWarnings("serial")
+  private volatile java.lang.Object vendor_ = "";
+  /**
+   * <code>string vendor = 2 [json_name = "vendor"];</code>
+   * @return The vendor.
+   */
+  @java.lang.Override
+  public java.lang.String getVendor() {
+    java.lang.Object ref = vendor_;
+    if (ref instanceof java.lang.String) {
+      return (java.lang.String) ref;
+    } else {
+      com.google.protobuf.ByteString bs = 
+          (com.google.protobuf.ByteString) ref;
+      java.lang.String s = bs.toStringUtf8();
+      vendor_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string vendor = 2 [json_name = "vendor"];</code>
+   * @return The bytes for vendor.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getVendorBytes() {
+    java.lang.Object ref = vendor_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      vendor_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  public static final int VERSION_FIELD_NUMBER = 3;
+  @SuppressWarnings("serial")
+  private volatile java.lang.Object version_ = "";
+  /**
+   * <code>string version = 3 [json_name = "version"];</code>
+   * @return The version.
+   */
+  @java.lang.Override
+  public java.lang.String getVersion() {
+    java.lang.Object ref = version_;
+    if (ref instanceof java.lang.String) {
+      return (java.lang.String) ref;
+    } else {
+      com.google.protobuf.ByteString bs = 
+          (com.google.protobuf.ByteString) ref;
+      java.lang.String s = bs.toStringUtf8();
+      version_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string version = 3 [json_name = "version"];</code>
+   * @return The bytes for version.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getVersionBytes() {
+    java.lang.Object ref = version_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      version_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  public static final int KEYGENERATIONRATE_FIELD_NUMBER = 4;
+  private long keyGenerationRate_ = 0L;
+  /**
+   * <code>uint64 keyGenerationRate = 4 [json_name = "keyGenerationRate"];</code>
+   * @return The keyGenerationRate.
+   */
+  @java.lang.Override
+  public long getKeyGenerationRate() {
+    return keyGenerationRate_;
+  }
+
+  public static final int SOURCEID_FIELD_NUMBER = 5;
+  @SuppressWarnings("serial")
+  private volatile java.lang.Object sourceId_ = "";
+  /**
+   * <code>string sourceId = 5 [json_name = "sourceId"];</code>
+   * @return The sourceId.
+   */
+  @java.lang.Override
+  public java.lang.String getSourceId() {
+    java.lang.Object ref = sourceId_;
+    if (ref instanceof java.lang.String) {
+      return (java.lang.String) ref;
+    } else {
+      com.google.protobuf.ByteString bs = 
+          (com.google.protobuf.ByteString) ref;
+      java.lang.String s = bs.toStringUtf8();
+      sourceId_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string sourceId = 5 [json_name = "sourceId"];</code>
+   * @return The bytes for sourceId.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getSourceIdBytes() {
+    java.lang.Object ref = sourceId_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      sourceId_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  public static final int DESTINATIONID_FIELD_NUMBER = 6;
+  @SuppressWarnings("serial")
+  private volatile java.lang.Object destinationId_ = "";
+  /**
+   * <pre>
+   * key generation rate?
+   * einfügen, beeinhaltet den aktuell Wert der KG, kann auch null sein
+   * source und destination ID der angrenzenden QKD-Module
+   *...
+   * </pre>
+   *
+   * <code>string destinationId = 6 [json_name = "destinationId"];</code>
+   * @return The destinationId.
+   */
+  @java.lang.Override
+  public java.lang.String getDestinationId() {
+    java.lang.Object ref = destinationId_;
+    if (ref instanceof java.lang.String) {
+      return (java.lang.String) ref;
+    } else {
+      com.google.protobuf.ByteString bs = 
+          (com.google.protobuf.ByteString) ref;
+      java.lang.String s = bs.toStringUtf8();
+      destinationId_ = s;
+      return s;
+    }
+  }
+  /**
+   * <pre>
+   * key generation rate?
+   * einfügen, beeinhaltet den aktuell Wert der KG, kann auch null sein
+   * source und destination ID der angrenzenden QKD-Module
+   *...
+   * </pre>
+   *
+   * <code>string destinationId = 6 [json_name = "destinationId"];</code>
+   * @return The bytes for destinationId.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getDestinationIdBytes() {
+    java.lang.Object ref = destinationId_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      destinationId_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (timestamp_ != 0L) {
+      output.writeInt64(1, timestamp_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(vendor_)) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 2, vendor_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 3, version_);
+    }
+    if (keyGenerationRate_ != 0L) {
+      output.writeUInt64(4, keyGenerationRate_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(sourceId_)) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 5, sourceId_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(destinationId_)) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 6, destinationId_);
+    }
+    getUnknownFields().writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (timestamp_ != 0L) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeInt64Size(1, timestamp_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(vendor_)) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, vendor_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(version_)) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, version_);
+    }
+    if (keyGenerationRate_ != 0L) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeUInt64Size(4, keyGenerationRate_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(sourceId_)) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(5, sourceId_);
+    }
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(destinationId_)) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(6, destinationId_);
+    }
+    size += getUnknownFields().getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.quipsec.v1.Metadata)) {
+      return super.equals(obj);
+    }
+    com.quipsec.v1.Metadata other = (com.quipsec.v1.Metadata) obj;
+
+    if (getTimestamp()
+        != other.getTimestamp()) return false;
+    if (!getVendor()
+        .equals(other.getVendor())) return false;
+    if (!getVersion()
+        .equals(other.getVersion())) return false;
+    if (getKeyGenerationRate()
+        != other.getKeyGenerationRate()) return false;
+    if (!getSourceId()
+        .equals(other.getSourceId())) return false;
+    if (!getDestinationId()
+        .equals(other.getDestinationId())) return false;
+    if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    hash = (37 * hash) + TIMESTAMP_FIELD_NUMBER;
+    hash = (53 * hash) + com.google.protobuf.Internal.hashLong(
+        getTimestamp());
+    hash = (37 * hash) + VENDOR_FIELD_NUMBER;
+    hash = (53 * hash) + getVendor().hashCode();
+    hash = (37 * hash) + VERSION_FIELD_NUMBER;
+    hash = (53 * hash) + getVersion().hashCode();
+    hash = (37 * hash) + KEYGENERATIONRATE_FIELD_NUMBER;
+    hash = (53 * hash) + com.google.protobuf.Internal.hashLong(
+        getKeyGenerationRate());
+    hash = (37 * hash) + SOURCEID_FIELD_NUMBER;
+    hash = (53 * hash) + getSourceId().hashCode();
+    hash = (37 * hash) + DESTINATIONID_FIELD_NUMBER;
+    hash = (53 * hash) + getDestinationId().hashCode();
+    hash = (29 * hash) + getUnknownFields().hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.quipsec.v1.Metadata parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.Metadata parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.Metadata parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.Metadata parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.Metadata parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.Metadata parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.Metadata parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.Metadata parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  public static com.quipsec.v1.Metadata parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+
+  public static com.quipsec.v1.Metadata parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.quipsec.v1.Metadata parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.Metadata parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.quipsec.v1.Metadata prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code quipsec.v1.Metadata}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:quipsec.v1.Metadata)
+      com.quipsec.v1.MetadataOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_Metadata_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_Metadata_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.quipsec.v1.Metadata.class, com.quipsec.v1.Metadata.Builder.class);
+    }
+
+    // Construct using com.quipsec.v1.Metadata.newBuilder()
+    private Builder() {
+
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      bitField0_ = 0;
+      timestamp_ = 0L;
+      vendor_ = "";
+      version_ = "";
+      keyGenerationRate_ = 0L;
+      sourceId_ = "";
+      destinationId_ = "";
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_Metadata_descriptor;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.Metadata getDefaultInstanceForType() {
+      return com.quipsec.v1.Metadata.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.Metadata build() {
+      com.quipsec.v1.Metadata result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.Metadata buildPartial() {
+      com.quipsec.v1.Metadata result = new com.quipsec.v1.Metadata(this);
+      if (bitField0_ != 0) { buildPartial0(result); }
+      onBuilt();
+      return result;
+    }
+
+    private void buildPartial0(com.quipsec.v1.Metadata result) {
+      int from_bitField0_ = bitField0_;
+      if (((from_bitField0_ & 0x00000001) != 0)) {
+        result.timestamp_ = timestamp_;
+      }
+      if (((from_bitField0_ & 0x00000002) != 0)) {
+        result.vendor_ = vendor_;
+      }
+      if (((from_bitField0_ & 0x00000004) != 0)) {
+        result.version_ = version_;
+      }
+      if (((from_bitField0_ & 0x00000008) != 0)) {
+        result.keyGenerationRate_ = keyGenerationRate_;
+      }
+      if (((from_bitField0_ & 0x00000010) != 0)) {
+        result.sourceId_ = sourceId_;
+      }
+      if (((from_bitField0_ & 0x00000020) != 0)) {
+        result.destinationId_ = destinationId_;
+      }
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.quipsec.v1.Metadata) {
+        return mergeFrom((com.quipsec.v1.Metadata)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.quipsec.v1.Metadata other) {
+      if (other == com.quipsec.v1.Metadata.getDefaultInstance()) return this;
+      if (other.getTimestamp() != 0L) {
+        setTimestamp(other.getTimestamp());
+      }
+      if (!other.getVendor().isEmpty()) {
+        vendor_ = other.vendor_;
+        bitField0_ |= 0x00000002;
+        onChanged();
+      }
+      if (!other.getVersion().isEmpty()) {
+        version_ = other.version_;
+        bitField0_ |= 0x00000004;
+        onChanged();
+      }
+      if (other.getKeyGenerationRate() != 0L) {
+        setKeyGenerationRate(other.getKeyGenerationRate());
+      }
+      if (!other.getSourceId().isEmpty()) {
+        sourceId_ = other.sourceId_;
+        bitField0_ |= 0x00000010;
+        onChanged();
+      }
+      if (!other.getDestinationId().isEmpty()) {
+        destinationId_ = other.destinationId_;
+        bitField0_ |= 0x00000020;
+        onChanged();
+      }
+      this.mergeUnknownFields(other.getUnknownFields());
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            case 8: {
+              timestamp_ = input.readInt64();
+              bitField0_ |= 0x00000001;
+              break;
+            } // case 8
+            case 18: {
+              vendor_ = input.readStringRequireUtf8();
+              bitField0_ |= 0x00000002;
+              break;
+            } // case 18
+            case 26: {
+              version_ = input.readStringRequireUtf8();
+              bitField0_ |= 0x00000004;
+              break;
+            } // case 26
+            case 32: {
+              keyGenerationRate_ = input.readUInt64();
+              bitField0_ |= 0x00000008;
+              break;
+            } // case 32
+            case 42: {
+              sourceId_ = input.readStringRequireUtf8();
+              bitField0_ |= 0x00000010;
+              break;
+            } // case 42
+            case 50: {
+              destinationId_ = input.readStringRequireUtf8();
+              bitField0_ |= 0x00000020;
+              break;
+            } // case 50
+            default: {
+              if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                done = true; // was an endgroup tag
+              }
+              break;
+            } // default:
+          } // switch (tag)
+        } // while (!done)
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.unwrapIOException();
+      } finally {
+        onChanged();
+      } // finally
+      return this;
+    }
+    private int bitField0_;
+
+    private long timestamp_ ;
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp"];</code>
+     * @return The timestamp.
+     */
+    @java.lang.Override
+    public long getTimestamp() {
+      return timestamp_;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp"];</code>
+     * @param value The timestamp to set.
+     * @return This builder for chaining.
+     */
+    public Builder setTimestamp(long value) {
+
+      timestamp_ = value;
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp"];</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearTimestamp() {
+      bitField0_ = (bitField0_ & ~0x00000001);
+      timestamp_ = 0L;
+      onChanged();
+      return this;
+    }
+
+    private java.lang.Object vendor_ = "";
+    /**
+     * <code>string vendor = 2 [json_name = "vendor"];</code>
+     * @return The vendor.
+     */
+    public java.lang.String getVendor() {
+      java.lang.Object ref = vendor_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        vendor_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string vendor = 2 [json_name = "vendor"];</code>
+     * @return The bytes for vendor.
+     */
+    public com.google.protobuf.ByteString
+        getVendorBytes() {
+      java.lang.Object ref = vendor_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        vendor_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string vendor = 2 [json_name = "vendor"];</code>
+     * @param value The vendor to set.
+     * @return This builder for chaining.
+     */
+    public Builder setVendor(
+        java.lang.String value) {
+      if (value == null) { throw new NullPointerException(); }
+      vendor_ = value;
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string vendor = 2 [json_name = "vendor"];</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearVendor() {
+      vendor_ = getDefaultInstance().getVendor();
+      bitField0_ = (bitField0_ & ~0x00000002);
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string vendor = 2 [json_name = "vendor"];</code>
+     * @param value The bytes for vendor to set.
+     * @return This builder for chaining.
+     */
+    public Builder setVendorBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) { throw new NullPointerException(); }
+      checkByteStringIsUtf8(value);
+      vendor_ = value;
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+
+    private java.lang.Object version_ = "";
+    /**
+     * <code>string version = 3 [json_name = "version"];</code>
+     * @return The version.
+     */
+    public java.lang.String getVersion() {
+      java.lang.Object ref = version_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        version_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string version = 3 [json_name = "version"];</code>
+     * @return The bytes for version.
+     */
+    public com.google.protobuf.ByteString
+        getVersionBytes() {
+      java.lang.Object ref = version_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        version_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string version = 3 [json_name = "version"];</code>
+     * @param value The version to set.
+     * @return This builder for chaining.
+     */
+    public Builder setVersion(
+        java.lang.String value) {
+      if (value == null) { throw new NullPointerException(); }
+      version_ = value;
+      bitField0_ |= 0x00000004;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string version = 3 [json_name = "version"];</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearVersion() {
+      version_ = getDefaultInstance().getVersion();
+      bitField0_ = (bitField0_ & ~0x00000004);
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string version = 3 [json_name = "version"];</code>
+     * @param value The bytes for version to set.
+     * @return This builder for chaining.
+     */
+    public Builder setVersionBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) { throw new NullPointerException(); }
+      checkByteStringIsUtf8(value);
+      version_ = value;
+      bitField0_ |= 0x00000004;
+      onChanged();
+      return this;
+    }
+
+    private long keyGenerationRate_ ;
+    /**
+     * <code>uint64 keyGenerationRate = 4 [json_name = "keyGenerationRate"];</code>
+     * @return The keyGenerationRate.
+     */
+    @java.lang.Override
+    public long getKeyGenerationRate() {
+      return keyGenerationRate_;
+    }
+    /**
+     * <code>uint64 keyGenerationRate = 4 [json_name = "keyGenerationRate"];</code>
+     * @param value The keyGenerationRate to set.
+     * @return This builder for chaining.
+     */
+    public Builder setKeyGenerationRate(long value) {
+
+      keyGenerationRate_ = value;
+      bitField0_ |= 0x00000008;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>uint64 keyGenerationRate = 4 [json_name = "keyGenerationRate"];</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearKeyGenerationRate() {
+      bitField0_ = (bitField0_ & ~0x00000008);
+      keyGenerationRate_ = 0L;
+      onChanged();
+      return this;
+    }
+
+    private java.lang.Object sourceId_ = "";
+    /**
+     * <code>string sourceId = 5 [json_name = "sourceId"];</code>
+     * @return The sourceId.
+     */
+    public java.lang.String getSourceId() {
+      java.lang.Object ref = sourceId_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        sourceId_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string sourceId = 5 [json_name = "sourceId"];</code>
+     * @return The bytes for sourceId.
+     */
+    public com.google.protobuf.ByteString
+        getSourceIdBytes() {
+      java.lang.Object ref = sourceId_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        sourceId_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string sourceId = 5 [json_name = "sourceId"];</code>
+     * @param value The sourceId to set.
+     * @return This builder for chaining.
+     */
+    public Builder setSourceId(
+        java.lang.String value) {
+      if (value == null) { throw new NullPointerException(); }
+      sourceId_ = value;
+      bitField0_ |= 0x00000010;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string sourceId = 5 [json_name = "sourceId"];</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearSourceId() {
+      sourceId_ = getDefaultInstance().getSourceId();
+      bitField0_ = (bitField0_ & ~0x00000010);
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string sourceId = 5 [json_name = "sourceId"];</code>
+     * @param value The bytes for sourceId to set.
+     * @return This builder for chaining.
+     */
+    public Builder setSourceIdBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) { throw new NullPointerException(); }
+      checkByteStringIsUtf8(value);
+      sourceId_ = value;
+      bitField0_ |= 0x00000010;
+      onChanged();
+      return this;
+    }
+
+    private java.lang.Object destinationId_ = "";
+    /**
+     * <pre>
+     * key generation rate?
+     * einfügen, beeinhaltet den aktuell Wert der KG, kann auch null sein
+     * source und destination ID der angrenzenden QKD-Module
+     *...
+     * </pre>
+     *
+     * <code>string destinationId = 6 [json_name = "destinationId"];</code>
+     * @return The destinationId.
+     */
+    public java.lang.String getDestinationId() {
+      java.lang.Object ref = destinationId_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        destinationId_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <pre>
+     * key generation rate?
+     * einfügen, beeinhaltet den aktuell Wert der KG, kann auch null sein
+     * source und destination ID der angrenzenden QKD-Module
+     *...
+     * </pre>
+     *
+     * <code>string destinationId = 6 [json_name = "destinationId"];</code>
+     * @return The bytes for destinationId.
+     */
+    public com.google.protobuf.ByteString
+        getDestinationIdBytes() {
+      java.lang.Object ref = destinationId_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        destinationId_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <pre>
+     * key generation rate?
+     * einfügen, beeinhaltet den aktuell Wert der KG, kann auch null sein
+     * source und destination ID der angrenzenden QKD-Module
+     *...
+     * </pre>
+     *
+     * <code>string destinationId = 6 [json_name = "destinationId"];</code>
+     * @param value The destinationId to set.
+     * @return This builder for chaining.
+     */
+    public Builder setDestinationId(
+        java.lang.String value) {
+      if (value == null) { throw new NullPointerException(); }
+      destinationId_ = value;
+      bitField0_ |= 0x00000020;
+      onChanged();
+      return this;
+    }
+    /**
+     * <pre>
+     * key generation rate?
+     * einfügen, beeinhaltet den aktuell Wert der KG, kann auch null sein
+     * source und destination ID der angrenzenden QKD-Module
+     *...
+     * </pre>
+     *
+     * <code>string destinationId = 6 [json_name = "destinationId"];</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearDestinationId() {
+      destinationId_ = getDefaultInstance().getDestinationId();
+      bitField0_ = (bitField0_ & ~0x00000020);
+      onChanged();
+      return this;
+    }
+    /**
+     * <pre>
+     * key generation rate?
+     * einfügen, beeinhaltet den aktuell Wert der KG, kann auch null sein
+     * source und destination ID der angrenzenden QKD-Module
+     *...
+     * </pre>
+     *
+     * <code>string destinationId = 6 [json_name = "destinationId"];</code>
+     * @param value The bytes for destinationId to set.
+     * @return This builder for chaining.
+     */
+    public Builder setDestinationIdBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) { throw new NullPointerException(); }
+      checkByteStringIsUtf8(value);
+      destinationId_ = value;
+      bitField0_ |= 0x00000020;
+      onChanged();
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:quipsec.v1.Metadata)
+  }
+
+  // @@protoc_insertion_point(class_scope:quipsec.v1.Metadata)
+  private static final com.quipsec.v1.Metadata DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.quipsec.v1.Metadata();
+  }
+
+  public static com.quipsec.v1.Metadata getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<Metadata>
+      PARSER = new com.google.protobuf.AbstractParser<Metadata>() {
+    @java.lang.Override
+    public Metadata parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      Builder builder = newBuilder();
+      try {
+        builder.mergeFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(builder.buildPartial());
+      } catch (com.google.protobuf.UninitializedMessageException e) {
+        throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(e)
+            .setUnfinishedMessage(builder.buildPartial());
+      }
+      return builder.buildPartial();
+    }
+  };
+
+  public static com.google.protobuf.Parser<Metadata> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<Metadata> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.quipsec.v1.Metadata getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+
diff --git a/gen/java/com/quipsec/v1/MetadataOrBuilder.java b/gen/java/com/quipsec/v1/MetadataOrBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..a5b38749b80e300aed71a1b1553bd8cecab97cc7
--- /dev/null
+++ b/gen/java/com/quipsec/v1/MetadataOrBuilder.java
@@ -0,0 +1,84 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+public interface MetadataOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:quipsec.v1.Metadata)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp"];</code>
+   * @return The timestamp.
+   */
+  long getTimestamp();
+
+  /**
+   * <code>string vendor = 2 [json_name = "vendor"];</code>
+   * @return The vendor.
+   */
+  java.lang.String getVendor();
+  /**
+   * <code>string vendor = 2 [json_name = "vendor"];</code>
+   * @return The bytes for vendor.
+   */
+  com.google.protobuf.ByteString
+      getVendorBytes();
+
+  /**
+   * <code>string version = 3 [json_name = "version"];</code>
+   * @return The version.
+   */
+  java.lang.String getVersion();
+  /**
+   * <code>string version = 3 [json_name = "version"];</code>
+   * @return The bytes for version.
+   */
+  com.google.protobuf.ByteString
+      getVersionBytes();
+
+  /**
+   * <code>uint64 keyGenerationRate = 4 [json_name = "keyGenerationRate"];</code>
+   * @return The keyGenerationRate.
+   */
+  long getKeyGenerationRate();
+
+  /**
+   * <code>string sourceId = 5 [json_name = "sourceId"];</code>
+   * @return The sourceId.
+   */
+  java.lang.String getSourceId();
+  /**
+   * <code>string sourceId = 5 [json_name = "sourceId"];</code>
+   * @return The bytes for sourceId.
+   */
+  com.google.protobuf.ByteString
+      getSourceIdBytes();
+
+  /**
+   * <pre>
+   * key generation rate?
+   * einfügen, beeinhaltet den aktuell Wert der KG, kann auch null sein
+   * source und destination ID der angrenzenden QKD-Module
+   *...
+   * </pre>
+   *
+   * <code>string destinationId = 6 [json_name = "destinationId"];</code>
+   * @return The destinationId.
+   */
+  java.lang.String getDestinationId();
+  /**
+   * <pre>
+   * key generation rate?
+   * einfügen, beeinhaltet den aktuell Wert der KG, kann auch null sein
+   * source und destination ID der angrenzenden QKD-Module
+   *...
+   * </pre>
+   *
+   * <code>string destinationId = 6 [json_name = "destinationId"];</code>
+   * @return The bytes for destinationId.
+   */
+  com.google.protobuf.ByteString
+      getDestinationIdBytes();
+}
diff --git a/gen/java/com/quipsec/v1/PushKeysRequest.java b/gen/java/com/quipsec/v1/PushKeysRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b05c1acef3811d1178d10d4172ee8d154f144e7c
--- /dev/null
+++ b/gen/java/com/quipsec/v1/PushKeysRequest.java
@@ -0,0 +1,666 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+/**
+ * Protobuf type {@code quipsec.v1.PushKeysRequest}
+ */
+public final class PushKeysRequest extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:quipsec.v1.PushKeysRequest)
+    PushKeysRequestOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use PushKeysRequest.newBuilder() to construct.
+  private PushKeysRequest(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private PushKeysRequest() {
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new PushKeysRequest();
+  }
+
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_PushKeysRequest_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_PushKeysRequest_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.quipsec.v1.PushKeysRequest.class, com.quipsec.v1.PushKeysRequest.Builder.class);
+  }
+
+  private int bitField0_;
+  public static final int TIMESTAMP_FIELD_NUMBER = 1;
+  private long timestamp_ = 0L;
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+   * @return The timestamp.
+   */
+  @java.lang.Override
+  public long getTimestamp() {
+    return timestamp_;
+  }
+
+  public static final int KEYBULK_FIELD_NUMBER = 2;
+  private com.quipsec.v1.KeyBulk keyBulk_;
+  /**
+   * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+   * @return Whether the keyBulk field is set.
+   */
+  @java.lang.Override
+  public boolean hasKeyBulk() {
+    return ((bitField0_ & 0x00000001) != 0);
+  }
+  /**
+   * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+   * @return The keyBulk.
+   */
+  @java.lang.Override
+  public com.quipsec.v1.KeyBulk getKeyBulk() {
+    return keyBulk_ == null ? com.quipsec.v1.KeyBulk.getDefaultInstance() : keyBulk_;
+  }
+  /**
+   * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+   */
+  @java.lang.Override
+  public com.quipsec.v1.KeyBulkOrBuilder getKeyBulkOrBuilder() {
+    return keyBulk_ == null ? com.quipsec.v1.KeyBulk.getDefaultInstance() : keyBulk_;
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (timestamp_ != 0L) {
+      output.writeInt64(1, timestamp_);
+    }
+    if (((bitField0_ & 0x00000001) != 0)) {
+      output.writeMessage(2, getKeyBulk());
+    }
+    getUnknownFields().writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (timestamp_ != 0L) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeInt64Size(1, timestamp_);
+    }
+    if (((bitField0_ & 0x00000001) != 0)) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeMessageSize(2, getKeyBulk());
+    }
+    size += getUnknownFields().getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.quipsec.v1.PushKeysRequest)) {
+      return super.equals(obj);
+    }
+    com.quipsec.v1.PushKeysRequest other = (com.quipsec.v1.PushKeysRequest) obj;
+
+    if (getTimestamp()
+        != other.getTimestamp()) return false;
+    if (hasKeyBulk() != other.hasKeyBulk()) return false;
+    if (hasKeyBulk()) {
+      if (!getKeyBulk()
+          .equals(other.getKeyBulk())) return false;
+    }
+    if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    hash = (37 * hash) + TIMESTAMP_FIELD_NUMBER;
+    hash = (53 * hash) + com.google.protobuf.Internal.hashLong(
+        getTimestamp());
+    if (hasKeyBulk()) {
+      hash = (37 * hash) + KEYBULK_FIELD_NUMBER;
+      hash = (53 * hash) + getKeyBulk().hashCode();
+    }
+    hash = (29 * hash) + getUnknownFields().hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.quipsec.v1.PushKeysRequest parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.PushKeysRequest parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.PushKeysRequest parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.PushKeysRequest parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.PushKeysRequest parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.PushKeysRequest parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.PushKeysRequest parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.PushKeysRequest parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  public static com.quipsec.v1.PushKeysRequest parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+
+  public static com.quipsec.v1.PushKeysRequest parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.quipsec.v1.PushKeysRequest parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.PushKeysRequest parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.quipsec.v1.PushKeysRequest prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code quipsec.v1.PushKeysRequest}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:quipsec.v1.PushKeysRequest)
+      com.quipsec.v1.PushKeysRequestOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_PushKeysRequest_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_PushKeysRequest_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.quipsec.v1.PushKeysRequest.class, com.quipsec.v1.PushKeysRequest.Builder.class);
+    }
+
+    // Construct using com.quipsec.v1.PushKeysRequest.newBuilder()
+    private Builder() {
+      maybeForceBuilderInitialization();
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+      maybeForceBuilderInitialization();
+    }
+    private void maybeForceBuilderInitialization() {
+      if (com.google.protobuf.GeneratedMessageV3
+              .alwaysUseFieldBuilders) {
+        getKeyBulkFieldBuilder();
+      }
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      bitField0_ = 0;
+      timestamp_ = 0L;
+      keyBulk_ = null;
+      if (keyBulkBuilder_ != null) {
+        keyBulkBuilder_.dispose();
+        keyBulkBuilder_ = null;
+      }
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_PushKeysRequest_descriptor;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.PushKeysRequest getDefaultInstanceForType() {
+      return com.quipsec.v1.PushKeysRequest.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.PushKeysRequest build() {
+      com.quipsec.v1.PushKeysRequest result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.PushKeysRequest buildPartial() {
+      com.quipsec.v1.PushKeysRequest result = new com.quipsec.v1.PushKeysRequest(this);
+      if (bitField0_ != 0) { buildPartial0(result); }
+      onBuilt();
+      return result;
+    }
+
+    private void buildPartial0(com.quipsec.v1.PushKeysRequest result) {
+      int from_bitField0_ = bitField0_;
+      if (((from_bitField0_ & 0x00000001) != 0)) {
+        result.timestamp_ = timestamp_;
+      }
+      int to_bitField0_ = 0;
+      if (((from_bitField0_ & 0x00000002) != 0)) {
+        result.keyBulk_ = keyBulkBuilder_ == null
+            ? keyBulk_
+            : keyBulkBuilder_.build();
+        to_bitField0_ |= 0x00000001;
+      }
+      result.bitField0_ |= to_bitField0_;
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.quipsec.v1.PushKeysRequest) {
+        return mergeFrom((com.quipsec.v1.PushKeysRequest)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.quipsec.v1.PushKeysRequest other) {
+      if (other == com.quipsec.v1.PushKeysRequest.getDefaultInstance()) return this;
+      if (other.getTimestamp() != 0L) {
+        setTimestamp(other.getTimestamp());
+      }
+      if (other.hasKeyBulk()) {
+        mergeKeyBulk(other.getKeyBulk());
+      }
+      this.mergeUnknownFields(other.getUnknownFields());
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            case 8: {
+              timestamp_ = input.readInt64();
+              bitField0_ |= 0x00000001;
+              break;
+            } // case 8
+            case 18: {
+              input.readMessage(
+                  getKeyBulkFieldBuilder().getBuilder(),
+                  extensionRegistry);
+              bitField0_ |= 0x00000002;
+              break;
+            } // case 18
+            default: {
+              if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                done = true; // was an endgroup tag
+              }
+              break;
+            } // default:
+          } // switch (tag)
+        } // while (!done)
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.unwrapIOException();
+      } finally {
+        onChanged();
+      } // finally
+      return this;
+    }
+    private int bitField0_;
+
+    private long timestamp_ ;
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @return The timestamp.
+     */
+    @java.lang.Override
+    public long getTimestamp() {
+      return timestamp_;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @param value The timestamp to set.
+     * @return This builder for chaining.
+     */
+    public Builder setTimestamp(long value) {
+
+      timestamp_ = value;
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearTimestamp() {
+      bitField0_ = (bitField0_ & ~0x00000001);
+      timestamp_ = 0L;
+      onChanged();
+      return this;
+    }
+
+    private com.quipsec.v1.KeyBulk keyBulk_;
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.quipsec.v1.KeyBulk, com.quipsec.v1.KeyBulk.Builder, com.quipsec.v1.KeyBulkOrBuilder> keyBulkBuilder_;
+    /**
+     * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+     * @return Whether the keyBulk field is set.
+     */
+    public boolean hasKeyBulk() {
+      return ((bitField0_ & 0x00000002) != 0);
+    }
+    /**
+     * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+     * @return The keyBulk.
+     */
+    public com.quipsec.v1.KeyBulk getKeyBulk() {
+      if (keyBulkBuilder_ == null) {
+        return keyBulk_ == null ? com.quipsec.v1.KeyBulk.getDefaultInstance() : keyBulk_;
+      } else {
+        return keyBulkBuilder_.getMessage();
+      }
+    }
+    /**
+     * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+     */
+    public Builder setKeyBulk(com.quipsec.v1.KeyBulk value) {
+      if (keyBulkBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        keyBulk_ = value;
+      } else {
+        keyBulkBuilder_.setMessage(value);
+      }
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+     */
+    public Builder setKeyBulk(
+        com.quipsec.v1.KeyBulk.Builder builderForValue) {
+      if (keyBulkBuilder_ == null) {
+        keyBulk_ = builderForValue.build();
+      } else {
+        keyBulkBuilder_.setMessage(builderForValue.build());
+      }
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+     */
+    public Builder mergeKeyBulk(com.quipsec.v1.KeyBulk value) {
+      if (keyBulkBuilder_ == null) {
+        if (((bitField0_ & 0x00000002) != 0) &&
+          keyBulk_ != null &&
+          keyBulk_ != com.quipsec.v1.KeyBulk.getDefaultInstance()) {
+          getKeyBulkBuilder().mergeFrom(value);
+        } else {
+          keyBulk_ = value;
+        }
+      } else {
+        keyBulkBuilder_.mergeFrom(value);
+      }
+      if (keyBulk_ != null) {
+        bitField0_ |= 0x00000002;
+        onChanged();
+      }
+      return this;
+    }
+    /**
+     * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+     */
+    public Builder clearKeyBulk() {
+      bitField0_ = (bitField0_ & ~0x00000002);
+      keyBulk_ = null;
+      if (keyBulkBuilder_ != null) {
+        keyBulkBuilder_.dispose();
+        keyBulkBuilder_ = null;
+      }
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+     */
+    public com.quipsec.v1.KeyBulk.Builder getKeyBulkBuilder() {
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return getKeyBulkFieldBuilder().getBuilder();
+    }
+    /**
+     * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+     */
+    public com.quipsec.v1.KeyBulkOrBuilder getKeyBulkOrBuilder() {
+      if (keyBulkBuilder_ != null) {
+        return keyBulkBuilder_.getMessageOrBuilder();
+      } else {
+        return keyBulk_ == null ?
+            com.quipsec.v1.KeyBulk.getDefaultInstance() : keyBulk_;
+      }
+    }
+    /**
+     * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+     */
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.quipsec.v1.KeyBulk, com.quipsec.v1.KeyBulk.Builder, com.quipsec.v1.KeyBulkOrBuilder> 
+        getKeyBulkFieldBuilder() {
+      if (keyBulkBuilder_ == null) {
+        keyBulkBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+            com.quipsec.v1.KeyBulk, com.quipsec.v1.KeyBulk.Builder, com.quipsec.v1.KeyBulkOrBuilder>(
+                getKeyBulk(),
+                getParentForChildren(),
+                isClean());
+        keyBulk_ = null;
+      }
+      return keyBulkBuilder_;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:quipsec.v1.PushKeysRequest)
+  }
+
+  // @@protoc_insertion_point(class_scope:quipsec.v1.PushKeysRequest)
+  private static final com.quipsec.v1.PushKeysRequest DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.quipsec.v1.PushKeysRequest();
+  }
+
+  public static com.quipsec.v1.PushKeysRequest getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<PushKeysRequest>
+      PARSER = new com.google.protobuf.AbstractParser<PushKeysRequest>() {
+    @java.lang.Override
+    public PushKeysRequest parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      Builder builder = newBuilder();
+      try {
+        builder.mergeFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(builder.buildPartial());
+      } catch (com.google.protobuf.UninitializedMessageException e) {
+        throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(e)
+            .setUnfinishedMessage(builder.buildPartial());
+      }
+      return builder.buildPartial();
+    }
+  };
+
+  public static com.google.protobuf.Parser<PushKeysRequest> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<PushKeysRequest> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.quipsec.v1.PushKeysRequest getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+
diff --git a/gen/java/com/quipsec/v1/PushKeysRequestOrBuilder.java b/gen/java/com/quipsec/v1/PushKeysRequestOrBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..67556de9763cf8a0f45d9936a6033acf925ac60e
--- /dev/null
+++ b/gen/java/com/quipsec/v1/PushKeysRequestOrBuilder.java
@@ -0,0 +1,31 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+public interface PushKeysRequestOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:quipsec.v1.PushKeysRequest)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+   * @return The timestamp.
+   */
+  long getTimestamp();
+
+  /**
+   * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+   * @return Whether the keyBulk field is set.
+   */
+  boolean hasKeyBulk();
+  /**
+   * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+   * @return The keyBulk.
+   */
+  com.quipsec.v1.KeyBulk getKeyBulk();
+  /**
+   * <code>.quipsec.v1.KeyBulk keyBulk = 2 [json_name = "keyBulk", (.buf.validate.field) = { ... }</code>
+   */
+  com.quipsec.v1.KeyBulkOrBuilder getKeyBulkOrBuilder();
+}
diff --git a/gen/java/com/quipsec/v1/PushKeysResponse.java b/gen/java/com/quipsec/v1/PushKeysResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..3301795095992f4a2eac63e9200660e352f27b5a
--- /dev/null
+++ b/gen/java/com/quipsec/v1/PushKeysResponse.java
@@ -0,0 +1,473 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+/**
+ * Protobuf type {@code quipsec.v1.PushKeysResponse}
+ */
+public final class PushKeysResponse extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:quipsec.v1.PushKeysResponse)
+    PushKeysResponseOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use PushKeysResponse.newBuilder() to construct.
+  private PushKeysResponse(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private PushKeysResponse() {
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new PushKeysResponse();
+  }
+
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_PushKeysResponse_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_PushKeysResponse_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.quipsec.v1.PushKeysResponse.class, com.quipsec.v1.PushKeysResponse.Builder.class);
+  }
+
+  public static final int TIMESTAMP_FIELD_NUMBER = 1;
+  private long timestamp_ = 0L;
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+   * @return The timestamp.
+   */
+  @java.lang.Override
+  public long getTimestamp() {
+    return timestamp_;
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (timestamp_ != 0L) {
+      output.writeInt64(1, timestamp_);
+    }
+    getUnknownFields().writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (timestamp_ != 0L) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeInt64Size(1, timestamp_);
+    }
+    size += getUnknownFields().getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.quipsec.v1.PushKeysResponse)) {
+      return super.equals(obj);
+    }
+    com.quipsec.v1.PushKeysResponse other = (com.quipsec.v1.PushKeysResponse) obj;
+
+    if (getTimestamp()
+        != other.getTimestamp()) return false;
+    if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    hash = (37 * hash) + TIMESTAMP_FIELD_NUMBER;
+    hash = (53 * hash) + com.google.protobuf.Internal.hashLong(
+        getTimestamp());
+    hash = (29 * hash) + getUnknownFields().hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.quipsec.v1.PushKeysResponse parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.PushKeysResponse parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.PushKeysResponse parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.PushKeysResponse parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.PushKeysResponse parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.PushKeysResponse parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.PushKeysResponse parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.PushKeysResponse parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  public static com.quipsec.v1.PushKeysResponse parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+
+  public static com.quipsec.v1.PushKeysResponse parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.quipsec.v1.PushKeysResponse parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.PushKeysResponse parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.quipsec.v1.PushKeysResponse prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code quipsec.v1.PushKeysResponse}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:quipsec.v1.PushKeysResponse)
+      com.quipsec.v1.PushKeysResponseOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_PushKeysResponse_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_PushKeysResponse_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.quipsec.v1.PushKeysResponse.class, com.quipsec.v1.PushKeysResponse.Builder.class);
+    }
+
+    // Construct using com.quipsec.v1.PushKeysResponse.newBuilder()
+    private Builder() {
+
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      bitField0_ = 0;
+      timestamp_ = 0L;
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_PushKeysResponse_descriptor;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.PushKeysResponse getDefaultInstanceForType() {
+      return com.quipsec.v1.PushKeysResponse.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.PushKeysResponse build() {
+      com.quipsec.v1.PushKeysResponse result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.PushKeysResponse buildPartial() {
+      com.quipsec.v1.PushKeysResponse result = new com.quipsec.v1.PushKeysResponse(this);
+      if (bitField0_ != 0) { buildPartial0(result); }
+      onBuilt();
+      return result;
+    }
+
+    private void buildPartial0(com.quipsec.v1.PushKeysResponse result) {
+      int from_bitField0_ = bitField0_;
+      if (((from_bitField0_ & 0x00000001) != 0)) {
+        result.timestamp_ = timestamp_;
+      }
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.quipsec.v1.PushKeysResponse) {
+        return mergeFrom((com.quipsec.v1.PushKeysResponse)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.quipsec.v1.PushKeysResponse other) {
+      if (other == com.quipsec.v1.PushKeysResponse.getDefaultInstance()) return this;
+      if (other.getTimestamp() != 0L) {
+        setTimestamp(other.getTimestamp());
+      }
+      this.mergeUnknownFields(other.getUnknownFields());
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            case 8: {
+              timestamp_ = input.readInt64();
+              bitField0_ |= 0x00000001;
+              break;
+            } // case 8
+            default: {
+              if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                done = true; // was an endgroup tag
+              }
+              break;
+            } // default:
+          } // switch (tag)
+        } // while (!done)
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.unwrapIOException();
+      } finally {
+        onChanged();
+      } // finally
+      return this;
+    }
+    private int bitField0_;
+
+    private long timestamp_ ;
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @return The timestamp.
+     */
+    @java.lang.Override
+    public long getTimestamp() {
+      return timestamp_;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @param value The timestamp to set.
+     * @return This builder for chaining.
+     */
+    public Builder setTimestamp(long value) {
+
+      timestamp_ = value;
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearTimestamp() {
+      bitField0_ = (bitField0_ & ~0x00000001);
+      timestamp_ = 0L;
+      onChanged();
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:quipsec.v1.PushKeysResponse)
+  }
+
+  // @@protoc_insertion_point(class_scope:quipsec.v1.PushKeysResponse)
+  private static final com.quipsec.v1.PushKeysResponse DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.quipsec.v1.PushKeysResponse();
+  }
+
+  public static com.quipsec.v1.PushKeysResponse getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<PushKeysResponse>
+      PARSER = new com.google.protobuf.AbstractParser<PushKeysResponse>() {
+    @java.lang.Override
+    public PushKeysResponse parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      Builder builder = newBuilder();
+      try {
+        builder.mergeFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(builder.buildPartial());
+      } catch (com.google.protobuf.UninitializedMessageException e) {
+        throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(e)
+            .setUnfinishedMessage(builder.buildPartial());
+      }
+      return builder.buildPartial();
+    }
+  };
+
+  public static com.google.protobuf.Parser<PushKeysResponse> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<PushKeysResponse> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.quipsec.v1.PushKeysResponse getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+
diff --git a/gen/java/com/quipsec/v1/PushKeysResponseOrBuilder.java b/gen/java/com/quipsec/v1/PushKeysResponseOrBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..66d4a6e46f7364636d89386314cd1a62c882d0cc
--- /dev/null
+++ b/gen/java/com/quipsec/v1/PushKeysResponseOrBuilder.java
@@ -0,0 +1,16 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+public interface PushKeysResponseOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:quipsec.v1.PushKeysResponse)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+   * @return The timestamp.
+   */
+  long getTimestamp();
+}
diff --git a/gen/java/com/quipsec/v1/QkdmMetadataRequest.java b/gen/java/com/quipsec/v1/QkdmMetadataRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f489fbd17b784201379eea344c32e0ff9d882289
--- /dev/null
+++ b/gen/java/com/quipsec/v1/QkdmMetadataRequest.java
@@ -0,0 +1,473 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+/**
+ * Protobuf type {@code quipsec.v1.QkdmMetadataRequest}
+ */
+public final class QkdmMetadataRequest extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:quipsec.v1.QkdmMetadataRequest)
+    QkdmMetadataRequestOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use QkdmMetadataRequest.newBuilder() to construct.
+  private QkdmMetadataRequest(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private QkdmMetadataRequest() {
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new QkdmMetadataRequest();
+  }
+
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_QkdmMetadataRequest_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_QkdmMetadataRequest_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.quipsec.v1.QkdmMetadataRequest.class, com.quipsec.v1.QkdmMetadataRequest.Builder.class);
+  }
+
+  public static final int TIMESTAMP_FIELD_NUMBER = 1;
+  private long timestamp_ = 0L;
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+   * @return The timestamp.
+   */
+  @java.lang.Override
+  public long getTimestamp() {
+    return timestamp_;
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (timestamp_ != 0L) {
+      output.writeInt64(1, timestamp_);
+    }
+    getUnknownFields().writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (timestamp_ != 0L) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeInt64Size(1, timestamp_);
+    }
+    size += getUnknownFields().getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.quipsec.v1.QkdmMetadataRequest)) {
+      return super.equals(obj);
+    }
+    com.quipsec.v1.QkdmMetadataRequest other = (com.quipsec.v1.QkdmMetadataRequest) obj;
+
+    if (getTimestamp()
+        != other.getTimestamp()) return false;
+    if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    hash = (37 * hash) + TIMESTAMP_FIELD_NUMBER;
+    hash = (53 * hash) + com.google.protobuf.Internal.hashLong(
+        getTimestamp());
+    hash = (29 * hash) + getUnknownFields().hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.quipsec.v1.QkdmMetadataRequest parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.QkdmMetadataRequest parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.QkdmMetadataRequest parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.QkdmMetadataRequest parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.QkdmMetadataRequest parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.QkdmMetadataRequest parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.QkdmMetadataRequest parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.QkdmMetadataRequest parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  public static com.quipsec.v1.QkdmMetadataRequest parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+
+  public static com.quipsec.v1.QkdmMetadataRequest parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.quipsec.v1.QkdmMetadataRequest parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.QkdmMetadataRequest parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.quipsec.v1.QkdmMetadataRequest prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code quipsec.v1.QkdmMetadataRequest}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:quipsec.v1.QkdmMetadataRequest)
+      com.quipsec.v1.QkdmMetadataRequestOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_QkdmMetadataRequest_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_QkdmMetadataRequest_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.quipsec.v1.QkdmMetadataRequest.class, com.quipsec.v1.QkdmMetadataRequest.Builder.class);
+    }
+
+    // Construct using com.quipsec.v1.QkdmMetadataRequest.newBuilder()
+    private Builder() {
+
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      bitField0_ = 0;
+      timestamp_ = 0L;
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_QkdmMetadataRequest_descriptor;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.QkdmMetadataRequest getDefaultInstanceForType() {
+      return com.quipsec.v1.QkdmMetadataRequest.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.QkdmMetadataRequest build() {
+      com.quipsec.v1.QkdmMetadataRequest result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.QkdmMetadataRequest buildPartial() {
+      com.quipsec.v1.QkdmMetadataRequest result = new com.quipsec.v1.QkdmMetadataRequest(this);
+      if (bitField0_ != 0) { buildPartial0(result); }
+      onBuilt();
+      return result;
+    }
+
+    private void buildPartial0(com.quipsec.v1.QkdmMetadataRequest result) {
+      int from_bitField0_ = bitField0_;
+      if (((from_bitField0_ & 0x00000001) != 0)) {
+        result.timestamp_ = timestamp_;
+      }
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.quipsec.v1.QkdmMetadataRequest) {
+        return mergeFrom((com.quipsec.v1.QkdmMetadataRequest)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.quipsec.v1.QkdmMetadataRequest other) {
+      if (other == com.quipsec.v1.QkdmMetadataRequest.getDefaultInstance()) return this;
+      if (other.getTimestamp() != 0L) {
+        setTimestamp(other.getTimestamp());
+      }
+      this.mergeUnknownFields(other.getUnknownFields());
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            case 8: {
+              timestamp_ = input.readInt64();
+              bitField0_ |= 0x00000001;
+              break;
+            } // case 8
+            default: {
+              if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                done = true; // was an endgroup tag
+              }
+              break;
+            } // default:
+          } // switch (tag)
+        } // while (!done)
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.unwrapIOException();
+      } finally {
+        onChanged();
+      } // finally
+      return this;
+    }
+    private int bitField0_;
+
+    private long timestamp_ ;
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @return The timestamp.
+     */
+    @java.lang.Override
+    public long getTimestamp() {
+      return timestamp_;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @param value The timestamp to set.
+     * @return This builder for chaining.
+     */
+    public Builder setTimestamp(long value) {
+
+      timestamp_ = value;
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearTimestamp() {
+      bitField0_ = (bitField0_ & ~0x00000001);
+      timestamp_ = 0L;
+      onChanged();
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:quipsec.v1.QkdmMetadataRequest)
+  }
+
+  // @@protoc_insertion_point(class_scope:quipsec.v1.QkdmMetadataRequest)
+  private static final com.quipsec.v1.QkdmMetadataRequest DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.quipsec.v1.QkdmMetadataRequest();
+  }
+
+  public static com.quipsec.v1.QkdmMetadataRequest getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<QkdmMetadataRequest>
+      PARSER = new com.google.protobuf.AbstractParser<QkdmMetadataRequest>() {
+    @java.lang.Override
+    public QkdmMetadataRequest parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      Builder builder = newBuilder();
+      try {
+        builder.mergeFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(builder.buildPartial());
+      } catch (com.google.protobuf.UninitializedMessageException e) {
+        throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(e)
+            .setUnfinishedMessage(builder.buildPartial());
+      }
+      return builder.buildPartial();
+    }
+  };
+
+  public static com.google.protobuf.Parser<QkdmMetadataRequest> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<QkdmMetadataRequest> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.quipsec.v1.QkdmMetadataRequest getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+
diff --git a/gen/java/com/quipsec/v1/QkdmMetadataRequestOrBuilder.java b/gen/java/com/quipsec/v1/QkdmMetadataRequestOrBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..a7113663093a37aaa1d3b5429d8e0fa4c376ea5f
--- /dev/null
+++ b/gen/java/com/quipsec/v1/QkdmMetadataRequestOrBuilder.java
@@ -0,0 +1,16 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+public interface QkdmMetadataRequestOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:quipsec.v1.QkdmMetadataRequest)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+   * @return The timestamp.
+   */
+  long getTimestamp();
+}
diff --git a/gen/java/com/quipsec/v1/QkdmMetadataResponse.java b/gen/java/com/quipsec/v1/QkdmMetadataResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..a628864cef4968a1763d7a5ea9601a710ba9ea9d
--- /dev/null
+++ b/gen/java/com/quipsec/v1/QkdmMetadataResponse.java
@@ -0,0 +1,666 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+/**
+ * Protobuf type {@code quipsec.v1.QkdmMetadataResponse}
+ */
+public final class QkdmMetadataResponse extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:quipsec.v1.QkdmMetadataResponse)
+    QkdmMetadataResponseOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use QkdmMetadataResponse.newBuilder() to construct.
+  private QkdmMetadataResponse(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private QkdmMetadataResponse() {
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new QkdmMetadataResponse();
+  }
+
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_QkdmMetadataResponse_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_QkdmMetadataResponse_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.quipsec.v1.QkdmMetadataResponse.class, com.quipsec.v1.QkdmMetadataResponse.Builder.class);
+  }
+
+  private int bitField0_;
+  public static final int TIMESTAMP_FIELD_NUMBER = 1;
+  private long timestamp_ = 0L;
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+   * @return The timestamp.
+   */
+  @java.lang.Override
+  public long getTimestamp() {
+    return timestamp_;
+  }
+
+  public static final int METADATA_FIELD_NUMBER = 2;
+  private com.quipsec.v1.Metadata metadata_;
+  /**
+   * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+   * @return Whether the metadata field is set.
+   */
+  @java.lang.Override
+  public boolean hasMetadata() {
+    return ((bitField0_ & 0x00000001) != 0);
+  }
+  /**
+   * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+   * @return The metadata.
+   */
+  @java.lang.Override
+  public com.quipsec.v1.Metadata getMetadata() {
+    return metadata_ == null ? com.quipsec.v1.Metadata.getDefaultInstance() : metadata_;
+  }
+  /**
+   * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+   */
+  @java.lang.Override
+  public com.quipsec.v1.MetadataOrBuilder getMetadataOrBuilder() {
+    return metadata_ == null ? com.quipsec.v1.Metadata.getDefaultInstance() : metadata_;
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (timestamp_ != 0L) {
+      output.writeInt64(1, timestamp_);
+    }
+    if (((bitField0_ & 0x00000001) != 0)) {
+      output.writeMessage(2, getMetadata());
+    }
+    getUnknownFields().writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (timestamp_ != 0L) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeInt64Size(1, timestamp_);
+    }
+    if (((bitField0_ & 0x00000001) != 0)) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeMessageSize(2, getMetadata());
+    }
+    size += getUnknownFields().getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.quipsec.v1.QkdmMetadataResponse)) {
+      return super.equals(obj);
+    }
+    com.quipsec.v1.QkdmMetadataResponse other = (com.quipsec.v1.QkdmMetadataResponse) obj;
+
+    if (getTimestamp()
+        != other.getTimestamp()) return false;
+    if (hasMetadata() != other.hasMetadata()) return false;
+    if (hasMetadata()) {
+      if (!getMetadata()
+          .equals(other.getMetadata())) return false;
+    }
+    if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    hash = (37 * hash) + TIMESTAMP_FIELD_NUMBER;
+    hash = (53 * hash) + com.google.protobuf.Internal.hashLong(
+        getTimestamp());
+    if (hasMetadata()) {
+      hash = (37 * hash) + METADATA_FIELD_NUMBER;
+      hash = (53 * hash) + getMetadata().hashCode();
+    }
+    hash = (29 * hash) + getUnknownFields().hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.quipsec.v1.QkdmMetadataResponse parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.QkdmMetadataResponse parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.QkdmMetadataResponse parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.QkdmMetadataResponse parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.QkdmMetadataResponse parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.QkdmMetadataResponse parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.QkdmMetadataResponse parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.QkdmMetadataResponse parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  public static com.quipsec.v1.QkdmMetadataResponse parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+
+  public static com.quipsec.v1.QkdmMetadataResponse parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.quipsec.v1.QkdmMetadataResponse parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.QkdmMetadataResponse parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.quipsec.v1.QkdmMetadataResponse prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code quipsec.v1.QkdmMetadataResponse}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:quipsec.v1.QkdmMetadataResponse)
+      com.quipsec.v1.QkdmMetadataResponseOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_QkdmMetadataResponse_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_QkdmMetadataResponse_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.quipsec.v1.QkdmMetadataResponse.class, com.quipsec.v1.QkdmMetadataResponse.Builder.class);
+    }
+
+    // Construct using com.quipsec.v1.QkdmMetadataResponse.newBuilder()
+    private Builder() {
+      maybeForceBuilderInitialization();
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+      maybeForceBuilderInitialization();
+    }
+    private void maybeForceBuilderInitialization() {
+      if (com.google.protobuf.GeneratedMessageV3
+              .alwaysUseFieldBuilders) {
+        getMetadataFieldBuilder();
+      }
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      bitField0_ = 0;
+      timestamp_ = 0L;
+      metadata_ = null;
+      if (metadataBuilder_ != null) {
+        metadataBuilder_.dispose();
+        metadataBuilder_ = null;
+      }
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_QkdmMetadataResponse_descriptor;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.QkdmMetadataResponse getDefaultInstanceForType() {
+      return com.quipsec.v1.QkdmMetadataResponse.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.QkdmMetadataResponse build() {
+      com.quipsec.v1.QkdmMetadataResponse result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.QkdmMetadataResponse buildPartial() {
+      com.quipsec.v1.QkdmMetadataResponse result = new com.quipsec.v1.QkdmMetadataResponse(this);
+      if (bitField0_ != 0) { buildPartial0(result); }
+      onBuilt();
+      return result;
+    }
+
+    private void buildPartial0(com.quipsec.v1.QkdmMetadataResponse result) {
+      int from_bitField0_ = bitField0_;
+      if (((from_bitField0_ & 0x00000001) != 0)) {
+        result.timestamp_ = timestamp_;
+      }
+      int to_bitField0_ = 0;
+      if (((from_bitField0_ & 0x00000002) != 0)) {
+        result.metadata_ = metadataBuilder_ == null
+            ? metadata_
+            : metadataBuilder_.build();
+        to_bitField0_ |= 0x00000001;
+      }
+      result.bitField0_ |= to_bitField0_;
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.quipsec.v1.QkdmMetadataResponse) {
+        return mergeFrom((com.quipsec.v1.QkdmMetadataResponse)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.quipsec.v1.QkdmMetadataResponse other) {
+      if (other == com.quipsec.v1.QkdmMetadataResponse.getDefaultInstance()) return this;
+      if (other.getTimestamp() != 0L) {
+        setTimestamp(other.getTimestamp());
+      }
+      if (other.hasMetadata()) {
+        mergeMetadata(other.getMetadata());
+      }
+      this.mergeUnknownFields(other.getUnknownFields());
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            case 8: {
+              timestamp_ = input.readInt64();
+              bitField0_ |= 0x00000001;
+              break;
+            } // case 8
+            case 18: {
+              input.readMessage(
+                  getMetadataFieldBuilder().getBuilder(),
+                  extensionRegistry);
+              bitField0_ |= 0x00000002;
+              break;
+            } // case 18
+            default: {
+              if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                done = true; // was an endgroup tag
+              }
+              break;
+            } // default:
+          } // switch (tag)
+        } // while (!done)
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.unwrapIOException();
+      } finally {
+        onChanged();
+      } // finally
+      return this;
+    }
+    private int bitField0_;
+
+    private long timestamp_ ;
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @return The timestamp.
+     */
+    @java.lang.Override
+    public long getTimestamp() {
+      return timestamp_;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @param value The timestamp to set.
+     * @return This builder for chaining.
+     */
+    public Builder setTimestamp(long value) {
+
+      timestamp_ = value;
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearTimestamp() {
+      bitField0_ = (bitField0_ & ~0x00000001);
+      timestamp_ = 0L;
+      onChanged();
+      return this;
+    }
+
+    private com.quipsec.v1.Metadata metadata_;
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.quipsec.v1.Metadata, com.quipsec.v1.Metadata.Builder, com.quipsec.v1.MetadataOrBuilder> metadataBuilder_;
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     * @return Whether the metadata field is set.
+     */
+    public boolean hasMetadata() {
+      return ((bitField0_ & 0x00000002) != 0);
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     * @return The metadata.
+     */
+    public com.quipsec.v1.Metadata getMetadata() {
+      if (metadataBuilder_ == null) {
+        return metadata_ == null ? com.quipsec.v1.Metadata.getDefaultInstance() : metadata_;
+      } else {
+        return metadataBuilder_.getMessage();
+      }
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    public Builder setMetadata(com.quipsec.v1.Metadata value) {
+      if (metadataBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        metadata_ = value;
+      } else {
+        metadataBuilder_.setMessage(value);
+      }
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    public Builder setMetadata(
+        com.quipsec.v1.Metadata.Builder builderForValue) {
+      if (metadataBuilder_ == null) {
+        metadata_ = builderForValue.build();
+      } else {
+        metadataBuilder_.setMessage(builderForValue.build());
+      }
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    public Builder mergeMetadata(com.quipsec.v1.Metadata value) {
+      if (metadataBuilder_ == null) {
+        if (((bitField0_ & 0x00000002) != 0) &&
+          metadata_ != null &&
+          metadata_ != com.quipsec.v1.Metadata.getDefaultInstance()) {
+          getMetadataBuilder().mergeFrom(value);
+        } else {
+          metadata_ = value;
+        }
+      } else {
+        metadataBuilder_.mergeFrom(value);
+      }
+      if (metadata_ != null) {
+        bitField0_ |= 0x00000002;
+        onChanged();
+      }
+      return this;
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    public Builder clearMetadata() {
+      bitField0_ = (bitField0_ & ~0x00000002);
+      metadata_ = null;
+      if (metadataBuilder_ != null) {
+        metadataBuilder_.dispose();
+        metadataBuilder_ = null;
+      }
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    public com.quipsec.v1.Metadata.Builder getMetadataBuilder() {
+      bitField0_ |= 0x00000002;
+      onChanged();
+      return getMetadataFieldBuilder().getBuilder();
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    public com.quipsec.v1.MetadataOrBuilder getMetadataOrBuilder() {
+      if (metadataBuilder_ != null) {
+        return metadataBuilder_.getMessageOrBuilder();
+      } else {
+        return metadata_ == null ?
+            com.quipsec.v1.Metadata.getDefaultInstance() : metadata_;
+      }
+    }
+    /**
+     * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+     */
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.quipsec.v1.Metadata, com.quipsec.v1.Metadata.Builder, com.quipsec.v1.MetadataOrBuilder> 
+        getMetadataFieldBuilder() {
+      if (metadataBuilder_ == null) {
+        metadataBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+            com.quipsec.v1.Metadata, com.quipsec.v1.Metadata.Builder, com.quipsec.v1.MetadataOrBuilder>(
+                getMetadata(),
+                getParentForChildren(),
+                isClean());
+        metadata_ = null;
+      }
+      return metadataBuilder_;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:quipsec.v1.QkdmMetadataResponse)
+  }
+
+  // @@protoc_insertion_point(class_scope:quipsec.v1.QkdmMetadataResponse)
+  private static final com.quipsec.v1.QkdmMetadataResponse DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.quipsec.v1.QkdmMetadataResponse();
+  }
+
+  public static com.quipsec.v1.QkdmMetadataResponse getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<QkdmMetadataResponse>
+      PARSER = new com.google.protobuf.AbstractParser<QkdmMetadataResponse>() {
+    @java.lang.Override
+    public QkdmMetadataResponse parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      Builder builder = newBuilder();
+      try {
+        builder.mergeFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(builder.buildPartial());
+      } catch (com.google.protobuf.UninitializedMessageException e) {
+        throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(e)
+            .setUnfinishedMessage(builder.buildPartial());
+      }
+      return builder.buildPartial();
+    }
+  };
+
+  public static com.google.protobuf.Parser<QkdmMetadataResponse> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<QkdmMetadataResponse> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.quipsec.v1.QkdmMetadataResponse getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+
diff --git a/gen/java/com/quipsec/v1/QkdmMetadataResponseOrBuilder.java b/gen/java/com/quipsec/v1/QkdmMetadataResponseOrBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..689c218a27a74347dcb5d41b3efffb50015a8ffa
--- /dev/null
+++ b/gen/java/com/quipsec/v1/QkdmMetadataResponseOrBuilder.java
@@ -0,0 +1,31 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+public interface QkdmMetadataResponseOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:quipsec.v1.QkdmMetadataResponse)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>int64 timestamp = 1 [json_name = "timestamp", (.buf.validate.field) = { ... }</code>
+   * @return The timestamp.
+   */
+  long getTimestamp();
+
+  /**
+   * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+   * @return Whether the metadata field is set.
+   */
+  boolean hasMetadata();
+  /**
+   * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+   * @return The metadata.
+   */
+  com.quipsec.v1.Metadata getMetadata();
+  /**
+   * <code>.quipsec.v1.Metadata metadata = 2 [json_name = "metadata", (.buf.validate.field) = { ... }</code>
+   */
+  com.quipsec.v1.MetadataOrBuilder getMetadataOrBuilder();
+}
diff --git a/gen/java/com/quipsec/v1/QuicsepProto.java b/gen/java/com/quipsec/v1/QuicsepProto.java
new file mode 100644
index 0000000000000000000000000000000000000000..7f4065c7953126e097a85ca1f66bbecf3e29176b
--- /dev/null
+++ b/gen/java/com/quipsec/v1/QuicsepProto.java
@@ -0,0 +1,194 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+public final class QuicsepProto {
+  private QuicsepProto() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistryLite registry) {
+  }
+
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistry registry) {
+    registerAllExtensions(
+        (com.google.protobuf.ExtensionRegistryLite) registry);
+  }
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_quipsec_v1_Metadata_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_quipsec_v1_Metadata_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_quipsec_v1_KeyBulk_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_quipsec_v1_KeyBulk_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_quipsec_v1_CapabilitiesRequest_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_quipsec_v1_CapabilitiesRequest_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_quipsec_v1_CapabilitiesResponse_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_quipsec_v1_CapabilitiesResponse_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_quipsec_v1_PushKeysRequest_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_quipsec_v1_PushKeysRequest_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_quipsec_v1_PushKeysResponse_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_quipsec_v1_PushKeysResponse_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_quipsec_v1_QkdmMetadataRequest_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_quipsec_v1_QkdmMetadataRequest_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_quipsec_v1_QkdmMetadataResponse_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_quipsec_v1_QkdmMetadataResponse_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_quipsec_v1_ShutdownNotificationRequest_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_quipsec_v1_ShutdownNotificationRequest_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_quipsec_v1_ShutdownNotificationResponse_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_quipsec_v1_ShutdownNotificationResponse_fieldAccessorTable;
+
+  public static com.google.protobuf.Descriptors.FileDescriptor
+      getDescriptor() {
+    return descriptor;
+  }
+  private static  com.google.protobuf.Descriptors.FileDescriptor
+      descriptor;
+  static {
+    java.lang.String[] descriptorData = {
+      "\n\025quicsep/quicsep.proto\022\nquipsec.v1\032\033buf" +
+      "/validate/validate.proto\"\312\001\n\010Metadata\022\034\n" +
+      "\ttimestamp\030\001 \001(\003R\ttimestamp\022\026\n\006vendor\030\002 " +
+      "\001(\tR\006vendor\022\030\n\007version\030\003 \001(\tR\007version\022,\n" +
+      "\021keyGenerationRate\030\004 \001(\004R\021keyGenerationR" +
+      "ate\022\032\n\010sourceId\030\005 \001(\tR\010sourceId\022$\n\rdesti" +
+      "nationId\030\006 \001(\tR\rdestinationId\"\305\001\n\007KeyBul" +
+      "k\022\034\n\005keyId\030\001 \001(\tB\006\272H\003\310\001\001R\005keyId\022\032\n\004keys\030" +
+      "\002 \001(\014B\006\272H\003\310\001\001R\004keys\022$\n\tkeyLength\030\003 \001(\004B\006" +
+      "\272H\003\310\001\001R\tkeyLength\022 \n\007keyHash\030\004 \001(\tB\006\272H\003\310" +
+      "\001\001R\007keyHash\0228\n\010metadata\030\005 \001(\0132\024.quipsec." +
+      "v1.MetadataB\006\272H\003\310\001\001R\010metadata\"]\n\023Capabil" +
+      "itiesRequest\022$\n\ttimestamp\030\001 \001(\003B\006\272H\003\310\001\001R" +
+      "\ttimestamp\022 \n\007version\030\002 \001(\tB\006\272H\003\310\001\001R\007ver" +
+      "sion\"^\n\024CapabilitiesResponse\022$\n\ttimestam" +
+      "p\030\001 \001(\003B\006\272H\003\310\001\001R\ttimestamp\022 \n\007version\030\002 " +
+      "\001(\tB\006\272H\003\310\001\001R\007version\"n\n\017PushKeysRequest\022" +
+      "$\n\ttimestamp\030\001 \001(\003B\006\272H\003\310\001\001R\ttimestamp\0225\n" +
+      "\007keyBulk\030\002 \001(\0132\023.quipsec.v1.KeyBulkB\006\272H\003" +
+      "\310\001\001R\007keyBulk\"8\n\020PushKeysResponse\022$\n\ttime" +
+      "stamp\030\001 \001(\003B\006\272H\003\310\001\001R\ttimestamp\";\n\023QkdmMe" +
+      "tadataRequest\022$\n\ttimestamp\030\001 \001(\003B\006\272H\003\310\001\001" +
+      "R\ttimestamp\"v\n\024QkdmMetadataResponse\022$\n\tt" +
+      "imestamp\030\001 \001(\003B\006\272H\003\310\001\001R\ttimestamp\0228\n\010met" +
+      "adata\030\002 \001(\0132\024.quipsec.v1.MetadataB\006\272H\003\310\001" +
+      "\001R\010metadata\"M\n\033ShutdownNotificationReque" +
+      "st\022.\n\016shutdownReason\030\001 \001(\tB\006\272H\003\310\001\001R\016shut" +
+      "downReason\"\036\n\034ShutdownNotificationRespon" +
+      "se2\375\002\n\033KmsQkdmCommunicationService\022S\n\014Ca" +
+      "pabilities\022\037.quipsec.v1.CapabilitiesRequ" +
+      "est\032 .quipsec.v1.CapabilitiesResponse\"\000\022" +
+      "G\n\010PushKeys\022\033.quipsec.v1.PushKeysRequest" +
+      "\032\034.quipsec.v1.PushKeysResponse\"\000\022S\n\014Qkdm" +
+      "Metadata\022\037.quipsec.v1.QkdmMetadataReques" +
+      "t\032 .quipsec.v1.QkdmMetadataResponse\"\000\022k\n" +
+      "\024ShutdownNotification\022\'.quipsec.v1.Shutd" +
+      "ownNotificationRequest\032(.quipsec.v1.Shut" +
+      "downNotificationResponse\"\000B\240\001\n\016com.quips" +
+      "ec.v1B\014QuicsepProtoP\001Z7code.fbi.h-da.de/" +
+      "danet/quicsep/gen/go/quicsep;quipsecv1\242\002" +
+      "\003QXX\252\002\nQuipsec.V1\312\002\nQuipsec\\V1\342\002\026Quipsec" +
+      "\\V1\\GPBMetadata\352\002\013Quipsec::V1b\006proto3"
+    };
+    descriptor = com.google.protobuf.Descriptors.FileDescriptor
+      .internalBuildGeneratedFileFrom(descriptorData,
+        new com.google.protobuf.Descriptors.FileDescriptor[] {
+          com.buf.validate.ValidateProto.getDescriptor(),
+        });
+    internal_static_quipsec_v1_Metadata_descriptor =
+      getDescriptor().getMessageTypes().get(0);
+    internal_static_quipsec_v1_Metadata_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_quipsec_v1_Metadata_descriptor,
+        new java.lang.String[] { "Timestamp", "Vendor", "Version", "KeyGenerationRate", "SourceId", "DestinationId", });
+    internal_static_quipsec_v1_KeyBulk_descriptor =
+      getDescriptor().getMessageTypes().get(1);
+    internal_static_quipsec_v1_KeyBulk_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_quipsec_v1_KeyBulk_descriptor,
+        new java.lang.String[] { "KeyId", "Keys", "KeyLength", "KeyHash", "Metadata", });
+    internal_static_quipsec_v1_CapabilitiesRequest_descriptor =
+      getDescriptor().getMessageTypes().get(2);
+    internal_static_quipsec_v1_CapabilitiesRequest_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_quipsec_v1_CapabilitiesRequest_descriptor,
+        new java.lang.String[] { "Timestamp", "Version", });
+    internal_static_quipsec_v1_CapabilitiesResponse_descriptor =
+      getDescriptor().getMessageTypes().get(3);
+    internal_static_quipsec_v1_CapabilitiesResponse_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_quipsec_v1_CapabilitiesResponse_descriptor,
+        new java.lang.String[] { "Timestamp", "Version", });
+    internal_static_quipsec_v1_PushKeysRequest_descriptor =
+      getDescriptor().getMessageTypes().get(4);
+    internal_static_quipsec_v1_PushKeysRequest_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_quipsec_v1_PushKeysRequest_descriptor,
+        new java.lang.String[] { "Timestamp", "KeyBulk", });
+    internal_static_quipsec_v1_PushKeysResponse_descriptor =
+      getDescriptor().getMessageTypes().get(5);
+    internal_static_quipsec_v1_PushKeysResponse_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_quipsec_v1_PushKeysResponse_descriptor,
+        new java.lang.String[] { "Timestamp", });
+    internal_static_quipsec_v1_QkdmMetadataRequest_descriptor =
+      getDescriptor().getMessageTypes().get(6);
+    internal_static_quipsec_v1_QkdmMetadataRequest_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_quipsec_v1_QkdmMetadataRequest_descriptor,
+        new java.lang.String[] { "Timestamp", });
+    internal_static_quipsec_v1_QkdmMetadataResponse_descriptor =
+      getDescriptor().getMessageTypes().get(7);
+    internal_static_quipsec_v1_QkdmMetadataResponse_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_quipsec_v1_QkdmMetadataResponse_descriptor,
+        new java.lang.String[] { "Timestamp", "Metadata", });
+    internal_static_quipsec_v1_ShutdownNotificationRequest_descriptor =
+      getDescriptor().getMessageTypes().get(8);
+    internal_static_quipsec_v1_ShutdownNotificationRequest_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_quipsec_v1_ShutdownNotificationRequest_descriptor,
+        new java.lang.String[] { "ShutdownReason", });
+    internal_static_quipsec_v1_ShutdownNotificationResponse_descriptor =
+      getDescriptor().getMessageTypes().get(9);
+    internal_static_quipsec_v1_ShutdownNotificationResponse_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_quipsec_v1_ShutdownNotificationResponse_descriptor,
+        new java.lang.String[] { });
+    com.google.protobuf.ExtensionRegistry registry =
+        com.google.protobuf.ExtensionRegistry.newInstance();
+    registry.add(com.buf.validate.ValidateProto.field);
+    com.google.protobuf.Descriptors.FileDescriptor
+        .internalUpdateFileDescriptor(descriptor, registry);
+    com.buf.validate.ValidateProto.getDescriptor();
+  }
+
+  // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/gen/java/com/quipsec/v1/ShutdownNotificationRequest.java b/gen/java/com/quipsec/v1/ShutdownNotificationRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..539209f5c0052951220a63eba5c668a3880099ef
--- /dev/null
+++ b/gen/java/com/quipsec/v1/ShutdownNotificationRequest.java
@@ -0,0 +1,542 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+/**
+ * Protobuf type {@code quipsec.v1.ShutdownNotificationRequest}
+ */
+public final class ShutdownNotificationRequest extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:quipsec.v1.ShutdownNotificationRequest)
+    ShutdownNotificationRequestOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use ShutdownNotificationRequest.newBuilder() to construct.
+  private ShutdownNotificationRequest(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private ShutdownNotificationRequest() {
+    shutdownReason_ = "";
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new ShutdownNotificationRequest();
+  }
+
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_ShutdownNotificationRequest_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_ShutdownNotificationRequest_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.quipsec.v1.ShutdownNotificationRequest.class, com.quipsec.v1.ShutdownNotificationRequest.Builder.class);
+  }
+
+  public static final int SHUTDOWNREASON_FIELD_NUMBER = 1;
+  @SuppressWarnings("serial")
+  private volatile java.lang.Object shutdownReason_ = "";
+  /**
+   * <code>string shutdownReason = 1 [json_name = "shutdownReason", (.buf.validate.field) = { ... }</code>
+   * @return The shutdownReason.
+   */
+  @java.lang.Override
+  public java.lang.String getShutdownReason() {
+    java.lang.Object ref = shutdownReason_;
+    if (ref instanceof java.lang.String) {
+      return (java.lang.String) ref;
+    } else {
+      com.google.protobuf.ByteString bs = 
+          (com.google.protobuf.ByteString) ref;
+      java.lang.String s = bs.toStringUtf8();
+      shutdownReason_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string shutdownReason = 1 [json_name = "shutdownReason", (.buf.validate.field) = { ... }</code>
+   * @return The bytes for shutdownReason.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getShutdownReasonBytes() {
+    java.lang.Object ref = shutdownReason_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      shutdownReason_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(shutdownReason_)) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 1, shutdownReason_);
+    }
+    getUnknownFields().writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(shutdownReason_)) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, shutdownReason_);
+    }
+    size += getUnknownFields().getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.quipsec.v1.ShutdownNotificationRequest)) {
+      return super.equals(obj);
+    }
+    com.quipsec.v1.ShutdownNotificationRequest other = (com.quipsec.v1.ShutdownNotificationRequest) obj;
+
+    if (!getShutdownReason()
+        .equals(other.getShutdownReason())) return false;
+    if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    hash = (37 * hash) + SHUTDOWNREASON_FIELD_NUMBER;
+    hash = (53 * hash) + getShutdownReason().hashCode();
+    hash = (29 * hash) + getUnknownFields().hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.quipsec.v1.ShutdownNotificationRequest parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.ShutdownNotificationRequest parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.ShutdownNotificationRequest parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.ShutdownNotificationRequest parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.ShutdownNotificationRequest parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.ShutdownNotificationRequest parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.ShutdownNotificationRequest parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.ShutdownNotificationRequest parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  public static com.quipsec.v1.ShutdownNotificationRequest parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+
+  public static com.quipsec.v1.ShutdownNotificationRequest parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.quipsec.v1.ShutdownNotificationRequest parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.ShutdownNotificationRequest parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.quipsec.v1.ShutdownNotificationRequest prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code quipsec.v1.ShutdownNotificationRequest}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:quipsec.v1.ShutdownNotificationRequest)
+      com.quipsec.v1.ShutdownNotificationRequestOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_ShutdownNotificationRequest_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_ShutdownNotificationRequest_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.quipsec.v1.ShutdownNotificationRequest.class, com.quipsec.v1.ShutdownNotificationRequest.Builder.class);
+    }
+
+    // Construct using com.quipsec.v1.ShutdownNotificationRequest.newBuilder()
+    private Builder() {
+
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      bitField0_ = 0;
+      shutdownReason_ = "";
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_ShutdownNotificationRequest_descriptor;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.ShutdownNotificationRequest getDefaultInstanceForType() {
+      return com.quipsec.v1.ShutdownNotificationRequest.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.ShutdownNotificationRequest build() {
+      com.quipsec.v1.ShutdownNotificationRequest result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.ShutdownNotificationRequest buildPartial() {
+      com.quipsec.v1.ShutdownNotificationRequest result = new com.quipsec.v1.ShutdownNotificationRequest(this);
+      if (bitField0_ != 0) { buildPartial0(result); }
+      onBuilt();
+      return result;
+    }
+
+    private void buildPartial0(com.quipsec.v1.ShutdownNotificationRequest result) {
+      int from_bitField0_ = bitField0_;
+      if (((from_bitField0_ & 0x00000001) != 0)) {
+        result.shutdownReason_ = shutdownReason_;
+      }
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.quipsec.v1.ShutdownNotificationRequest) {
+        return mergeFrom((com.quipsec.v1.ShutdownNotificationRequest)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.quipsec.v1.ShutdownNotificationRequest other) {
+      if (other == com.quipsec.v1.ShutdownNotificationRequest.getDefaultInstance()) return this;
+      if (!other.getShutdownReason().isEmpty()) {
+        shutdownReason_ = other.shutdownReason_;
+        bitField0_ |= 0x00000001;
+        onChanged();
+      }
+      this.mergeUnknownFields(other.getUnknownFields());
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            case 10: {
+              shutdownReason_ = input.readStringRequireUtf8();
+              bitField0_ |= 0x00000001;
+              break;
+            } // case 10
+            default: {
+              if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                done = true; // was an endgroup tag
+              }
+              break;
+            } // default:
+          } // switch (tag)
+        } // while (!done)
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.unwrapIOException();
+      } finally {
+        onChanged();
+      } // finally
+      return this;
+    }
+    private int bitField0_;
+
+    private java.lang.Object shutdownReason_ = "";
+    /**
+     * <code>string shutdownReason = 1 [json_name = "shutdownReason", (.buf.validate.field) = { ... }</code>
+     * @return The shutdownReason.
+     */
+    public java.lang.String getShutdownReason() {
+      java.lang.Object ref = shutdownReason_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        shutdownReason_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string shutdownReason = 1 [json_name = "shutdownReason", (.buf.validate.field) = { ... }</code>
+     * @return The bytes for shutdownReason.
+     */
+    public com.google.protobuf.ByteString
+        getShutdownReasonBytes() {
+      java.lang.Object ref = shutdownReason_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        shutdownReason_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string shutdownReason = 1 [json_name = "shutdownReason", (.buf.validate.field) = { ... }</code>
+     * @param value The shutdownReason to set.
+     * @return This builder for chaining.
+     */
+    public Builder setShutdownReason(
+        java.lang.String value) {
+      if (value == null) { throw new NullPointerException(); }
+      shutdownReason_ = value;
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string shutdownReason = 1 [json_name = "shutdownReason", (.buf.validate.field) = { ... }</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearShutdownReason() {
+      shutdownReason_ = getDefaultInstance().getShutdownReason();
+      bitField0_ = (bitField0_ & ~0x00000001);
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string shutdownReason = 1 [json_name = "shutdownReason", (.buf.validate.field) = { ... }</code>
+     * @param value The bytes for shutdownReason to set.
+     * @return This builder for chaining.
+     */
+    public Builder setShutdownReasonBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) { throw new NullPointerException(); }
+      checkByteStringIsUtf8(value);
+      shutdownReason_ = value;
+      bitField0_ |= 0x00000001;
+      onChanged();
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:quipsec.v1.ShutdownNotificationRequest)
+  }
+
+  // @@protoc_insertion_point(class_scope:quipsec.v1.ShutdownNotificationRequest)
+  private static final com.quipsec.v1.ShutdownNotificationRequest DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.quipsec.v1.ShutdownNotificationRequest();
+  }
+
+  public static com.quipsec.v1.ShutdownNotificationRequest getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<ShutdownNotificationRequest>
+      PARSER = new com.google.protobuf.AbstractParser<ShutdownNotificationRequest>() {
+    @java.lang.Override
+    public ShutdownNotificationRequest parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      Builder builder = newBuilder();
+      try {
+        builder.mergeFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(builder.buildPartial());
+      } catch (com.google.protobuf.UninitializedMessageException e) {
+        throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(e)
+            .setUnfinishedMessage(builder.buildPartial());
+      }
+      return builder.buildPartial();
+    }
+  };
+
+  public static com.google.protobuf.Parser<ShutdownNotificationRequest> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<ShutdownNotificationRequest> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.quipsec.v1.ShutdownNotificationRequest getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+
diff --git a/gen/java/com/quipsec/v1/ShutdownNotificationRequestOrBuilder.java b/gen/java/com/quipsec/v1/ShutdownNotificationRequestOrBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6491bcf5233d33b3079cc2d1ad6928bfd9360e4
--- /dev/null
+++ b/gen/java/com/quipsec/v1/ShutdownNotificationRequestOrBuilder.java
@@ -0,0 +1,22 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+public interface ShutdownNotificationRequestOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:quipsec.v1.ShutdownNotificationRequest)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>string shutdownReason = 1 [json_name = "shutdownReason", (.buf.validate.field) = { ... }</code>
+   * @return The shutdownReason.
+   */
+  java.lang.String getShutdownReason();
+  /**
+   * <code>string shutdownReason = 1 [json_name = "shutdownReason", (.buf.validate.field) = { ... }</code>
+   * @return The bytes for shutdownReason.
+   */
+  com.google.protobuf.ByteString
+      getShutdownReasonBytes();
+}
diff --git a/gen/java/com/quipsec/v1/ShutdownNotificationResponse.java b/gen/java/com/quipsec/v1/ShutdownNotificationResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..cec7bc483b2f62dd97a8198bccc8fce37e18a800
--- /dev/null
+++ b/gen/java/com/quipsec/v1/ShutdownNotificationResponse.java
@@ -0,0 +1,399 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+/**
+ * Protobuf type {@code quipsec.v1.ShutdownNotificationResponse}
+ */
+public final class ShutdownNotificationResponse extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:quipsec.v1.ShutdownNotificationResponse)
+    ShutdownNotificationResponseOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use ShutdownNotificationResponse.newBuilder() to construct.
+  private ShutdownNotificationResponse(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private ShutdownNotificationResponse() {
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new ShutdownNotificationResponse();
+  }
+
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_ShutdownNotificationResponse_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_ShutdownNotificationResponse_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.quipsec.v1.ShutdownNotificationResponse.class, com.quipsec.v1.ShutdownNotificationResponse.Builder.class);
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    getUnknownFields().writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    size += getUnknownFields().getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.quipsec.v1.ShutdownNotificationResponse)) {
+      return super.equals(obj);
+    }
+    com.quipsec.v1.ShutdownNotificationResponse other = (com.quipsec.v1.ShutdownNotificationResponse) obj;
+
+    if (!getUnknownFields().equals(other.getUnknownFields())) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    hash = (29 * hash) + getUnknownFields().hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.quipsec.v1.ShutdownNotificationResponse parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.ShutdownNotificationResponse parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.ShutdownNotificationResponse parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.ShutdownNotificationResponse parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.ShutdownNotificationResponse parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.quipsec.v1.ShutdownNotificationResponse parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.quipsec.v1.ShutdownNotificationResponse parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.ShutdownNotificationResponse parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  public static com.quipsec.v1.ShutdownNotificationResponse parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+
+  public static com.quipsec.v1.ShutdownNotificationResponse parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.quipsec.v1.ShutdownNotificationResponse parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.quipsec.v1.ShutdownNotificationResponse parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.quipsec.v1.ShutdownNotificationResponse prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code quipsec.v1.ShutdownNotificationResponse}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:quipsec.v1.ShutdownNotificationResponse)
+      com.quipsec.v1.ShutdownNotificationResponseOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_ShutdownNotificationResponse_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_ShutdownNotificationResponse_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.quipsec.v1.ShutdownNotificationResponse.class, com.quipsec.v1.ShutdownNotificationResponse.Builder.class);
+    }
+
+    // Construct using com.quipsec.v1.ShutdownNotificationResponse.newBuilder()
+    private Builder() {
+
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.quipsec.v1.QuicsepProto.internal_static_quipsec_v1_ShutdownNotificationResponse_descriptor;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.ShutdownNotificationResponse getDefaultInstanceForType() {
+      return com.quipsec.v1.ShutdownNotificationResponse.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.ShutdownNotificationResponse build() {
+      com.quipsec.v1.ShutdownNotificationResponse result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.quipsec.v1.ShutdownNotificationResponse buildPartial() {
+      com.quipsec.v1.ShutdownNotificationResponse result = new com.quipsec.v1.ShutdownNotificationResponse(this);
+      onBuilt();
+      return result;
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.quipsec.v1.ShutdownNotificationResponse) {
+        return mergeFrom((com.quipsec.v1.ShutdownNotificationResponse)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.quipsec.v1.ShutdownNotificationResponse other) {
+      if (other == com.quipsec.v1.ShutdownNotificationResponse.getDefaultInstance()) return this;
+      this.mergeUnknownFields(other.getUnknownFields());
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      if (extensionRegistry == null) {
+        throw new java.lang.NullPointerException();
+      }
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            default: {
+              if (!super.parseUnknownField(input, extensionRegistry, tag)) {
+                done = true; // was an endgroup tag
+              }
+              break;
+            } // default:
+          } // switch (tag)
+        } // while (!done)
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.unwrapIOException();
+      } finally {
+        onChanged();
+      } // finally
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:quipsec.v1.ShutdownNotificationResponse)
+  }
+
+  // @@protoc_insertion_point(class_scope:quipsec.v1.ShutdownNotificationResponse)
+  private static final com.quipsec.v1.ShutdownNotificationResponse DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.quipsec.v1.ShutdownNotificationResponse();
+  }
+
+  public static com.quipsec.v1.ShutdownNotificationResponse getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<ShutdownNotificationResponse>
+      PARSER = new com.google.protobuf.AbstractParser<ShutdownNotificationResponse>() {
+    @java.lang.Override
+    public ShutdownNotificationResponse parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      Builder builder = newBuilder();
+      try {
+        builder.mergeFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(builder.buildPartial());
+      } catch (com.google.protobuf.UninitializedMessageException e) {
+        throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(e)
+            .setUnfinishedMessage(builder.buildPartial());
+      }
+      return builder.buildPartial();
+    }
+  };
+
+  public static com.google.protobuf.Parser<ShutdownNotificationResponse> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<ShutdownNotificationResponse> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.quipsec.v1.ShutdownNotificationResponse getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+
diff --git a/gen/java/com/quipsec/v1/ShutdownNotificationResponseOrBuilder.java b/gen/java/com/quipsec/v1/ShutdownNotificationResponseOrBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..c08536cde7b511f28924f5667612a4f045530c19
--- /dev/null
+++ b/gen/java/com/quipsec/v1/ShutdownNotificationResponseOrBuilder.java
@@ -0,0 +1,10 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: quicsep/quicsep.proto
+
+// Protobuf Java Version: 3.25.0
+package com.quipsec.v1;
+
+public interface ShutdownNotificationResponseOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:quipsec.v1.ShutdownNotificationResponse)
+    com.google.protobuf.MessageOrBuilder {
+}
diff --git a/gen/python/quicsep/quicsep_pb2.py b/gen/python/quicsep/quicsep_pb2.py
index 3834d560efa8262792b18d81c280bd894f6d7a48..c6acca5b06fe7af639bb51a4aa59610e822b7da5 100644
--- a/gen/python/quicsep/quicsep_pb2.py
+++ b/gen/python/quicsep/quicsep_pb2.py
@@ -15,7 +15,7 @@ _sym_db = _symbol_database.Default()
 from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2
 
 
-DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15quicsep/quicsep.proto\x12\nquipsec.v1\x1a\x1b\x62uf/validate/validate.proto\"Z\n\x08Metadata\x12\x1c\n\ttimestamp\x18\x01 \x01(\x03R\ttimestamp\x12\x16\n\x06vendor\x18\x02 \x01(\tR\x06vendor\x12\x18\n\x07version\x18\x03 \x01(\tR\x07version\"\xc5\x01\n\x07KeyBulk\x12\x1c\n\x05keyId\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x05keyId\x12\x1a\n\x04keys\x18\x02 \x01(\x0c\x42\x06\xbaH\x03\xc8\x01\x01R\x04keys\x12$\n\tkeyLength\x18\x03 \x01(\x04\x42\x06\xbaH\x03\xc8\x01\x01R\tkeyLength\x12 \n\x07keyHash\x18\x04 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x07keyHash\x12\x38\n\x08metadata\x18\x05 \x01(\x0b\x32\x14.quipsec.v1.MetadataB\x06\xbaH\x03\xc8\x01\x01R\x08metadata\"7\n\x13\x43\x61pabilitiesRequest\x12 \n\x07version\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x07version\"8\n\x14\x43\x61pabilitiesResponse\x12 \n\x07version\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x07version\"n\n\x0fPushKeysRequest\x12$\n\ttimestamp\x18\x01 \x01(\x03\x42\x06\xbaH\x03\xc8\x01\x01R\ttimestamp\x12\x35\n\x07keyBulk\x18\x02 \x01(\x0b\x32\x13.quipsec.v1.KeyBulkB\x06\xbaH\x03\xc8\x01\x01R\x07keyBulk\"8\n\x10PushKeysResponse\x12$\n\ttimestamp\x18\x01 \x01(\x03\x42\x06\xbaH\x03\xc8\x01\x01R\ttimestamp\";\n\x13QkdmMetadataRequest\x12$\n\ttimestamp\x18\x01 \x01(\x03\x42\x06\xbaH\x03\xc8\x01\x01R\ttimestamp\"v\n\x14QkdmMetadataResponse\x12$\n\ttimestamp\x18\x01 \x01(\x03\x42\x06\xbaH\x03\xc8\x01\x01R\ttimestamp\x12\x38\n\x08metadata\x18\x02 \x01(\x0b\x32\x14.quipsec.v1.MetadataB\x06\xbaH\x03\xc8\x01\x01R\x08metadata\"M\n\x1bShutdownNotificationRequest\x12.\n\x0eshutdownReason\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x0eshutdownReason\"\x1e\n\x1cShutdownNotificationResponse2\xfd\x02\n\x1bKmsQkdmCommunicationService\x12S\n\x0c\x43\x61pabilities\x12\x1f.quipsec.v1.CapabilitiesRequest\x1a .quipsec.v1.CapabilitiesResponse\"\x00\x12G\n\x08PushKeys\x12\x1b.quipsec.v1.PushKeysRequest\x1a\x1c.quipsec.v1.PushKeysResponse\"\x00\x12S\n\x0cQkdmMetadata\x12\x1f.quipsec.v1.QkdmMetadataRequest\x1a .quipsec.v1.QkdmMetadataResponse\"\x00\x12k\n\x14ShutdownNotification\x12\'.quipsec.v1.ShutdownNotificationRequest\x1a(.quipsec.v1.ShutdownNotificationResponse\"\x00\x42\xa0\x01\n\x0e\x63om.quipsec.v1B\x0cQuicsepProtoP\x01Z7code.fbi.h-da.de/danet/quicsep/gen/go/quicsep;quipsecv1\xa2\x02\x03QXX\xaa\x02\nQuipsec.V1\xca\x02\nQuipsec\\V1\xe2\x02\x16Quipsec\\V1\\GPBMetadata\xea\x02\x0bQuipsec::V1b\x06proto3')
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15quicsep/quicsep.proto\x12\nquipsec.v1\x1a\x1b\x62uf/validate/validate.proto\"\xca\x01\n\x08Metadata\x12\x1c\n\ttimestamp\x18\x01 \x01(\x03R\ttimestamp\x12\x16\n\x06vendor\x18\x02 \x01(\tR\x06vendor\x12\x18\n\x07version\x18\x03 \x01(\tR\x07version\x12,\n\x11keyGenerationRate\x18\x04 \x01(\x04R\x11keyGenerationRate\x12\x1a\n\x08sourceId\x18\x05 \x01(\tR\x08sourceId\x12$\n\rdestinationId\x18\x06 \x01(\tR\rdestinationId\"\xc5\x01\n\x07KeyBulk\x12\x1c\n\x05keyId\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x05keyId\x12\x1a\n\x04keys\x18\x02 \x01(\x0c\x42\x06\xbaH\x03\xc8\x01\x01R\x04keys\x12$\n\tkeyLength\x18\x03 \x01(\x04\x42\x06\xbaH\x03\xc8\x01\x01R\tkeyLength\x12 \n\x07keyHash\x18\x04 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x07keyHash\x12\x38\n\x08metadata\x18\x05 \x01(\x0b\x32\x14.quipsec.v1.MetadataB\x06\xbaH\x03\xc8\x01\x01R\x08metadata\"]\n\x13\x43\x61pabilitiesRequest\x12$\n\ttimestamp\x18\x01 \x01(\x03\x42\x06\xbaH\x03\xc8\x01\x01R\ttimestamp\x12 \n\x07version\x18\x02 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x07version\"^\n\x14\x43\x61pabilitiesResponse\x12$\n\ttimestamp\x18\x01 \x01(\x03\x42\x06\xbaH\x03\xc8\x01\x01R\ttimestamp\x12 \n\x07version\x18\x02 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x07version\"n\n\x0fPushKeysRequest\x12$\n\ttimestamp\x18\x01 \x01(\x03\x42\x06\xbaH\x03\xc8\x01\x01R\ttimestamp\x12\x35\n\x07keyBulk\x18\x02 \x01(\x0b\x32\x13.quipsec.v1.KeyBulkB\x06\xbaH\x03\xc8\x01\x01R\x07keyBulk\"8\n\x10PushKeysResponse\x12$\n\ttimestamp\x18\x01 \x01(\x03\x42\x06\xbaH\x03\xc8\x01\x01R\ttimestamp\";\n\x13QkdmMetadataRequest\x12$\n\ttimestamp\x18\x01 \x01(\x03\x42\x06\xbaH\x03\xc8\x01\x01R\ttimestamp\"v\n\x14QkdmMetadataResponse\x12$\n\ttimestamp\x18\x01 \x01(\x03\x42\x06\xbaH\x03\xc8\x01\x01R\ttimestamp\x12\x38\n\x08metadata\x18\x02 \x01(\x0b\x32\x14.quipsec.v1.MetadataB\x06\xbaH\x03\xc8\x01\x01R\x08metadata\"M\n\x1bShutdownNotificationRequest\x12.\n\x0eshutdownReason\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x0eshutdownReason\"\x1e\n\x1cShutdownNotificationResponse2\xfd\x02\n\x1bKmsQkdmCommunicationService\x12S\n\x0c\x43\x61pabilities\x12\x1f.quipsec.v1.CapabilitiesRequest\x1a .quipsec.v1.CapabilitiesResponse\"\x00\x12G\n\x08PushKeys\x12\x1b.quipsec.v1.PushKeysRequest\x1a\x1c.quipsec.v1.PushKeysResponse\"\x00\x12S\n\x0cQkdmMetadata\x12\x1f.quipsec.v1.QkdmMetadataRequest\x1a .quipsec.v1.QkdmMetadataResponse\"\x00\x12k\n\x14ShutdownNotification\x12\'.quipsec.v1.ShutdownNotificationRequest\x1a(.quipsec.v1.ShutdownNotificationResponse\"\x00\x42\xa0\x01\n\x0e\x63om.quipsec.v1B\x0cQuicsepProtoP\x01Z7code.fbi.h-da.de/danet/quicsep/gen/go/quicsep;quipsecv1\xa2\x02\x03QXX\xaa\x02\nQuipsec.V1\xca\x02\nQuipsec\\V1\xe2\x02\x16Quipsec\\V1\\GPBMetadata\xea\x02\x0bQuipsec::V1b\x06proto3')
 
 _globals = globals()
 _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -33,8 +33,12 @@ if _descriptor._USE_C_DESCRIPTORS == False:
   _globals['_KEYBULK'].fields_by_name['keyHash']._serialized_options = b'\272H\003\310\001\001'
   _globals['_KEYBULK'].fields_by_name['metadata']._options = None
   _globals['_KEYBULK'].fields_by_name['metadata']._serialized_options = b'\272H\003\310\001\001'
+  _globals['_CAPABILITIESREQUEST'].fields_by_name['timestamp']._options = None
+  _globals['_CAPABILITIESREQUEST'].fields_by_name['timestamp']._serialized_options = b'\272H\003\310\001\001'
   _globals['_CAPABILITIESREQUEST'].fields_by_name['version']._options = None
   _globals['_CAPABILITIESREQUEST'].fields_by_name['version']._serialized_options = b'\272H\003\310\001\001'
+  _globals['_CAPABILITIESRESPONSE'].fields_by_name['timestamp']._options = None
+  _globals['_CAPABILITIESRESPONSE'].fields_by_name['timestamp']._serialized_options = b'\272H\003\310\001\001'
   _globals['_CAPABILITIESRESPONSE'].fields_by_name['version']._options = None
   _globals['_CAPABILITIESRESPONSE'].fields_by_name['version']._serialized_options = b'\272H\003\310\001\001'
   _globals['_PUSHKEYSREQUEST'].fields_by_name['timestamp']._options = None
@@ -51,26 +55,26 @@ if _descriptor._USE_C_DESCRIPTORS == False:
   _globals['_QKDMMETADATARESPONSE'].fields_by_name['metadata']._serialized_options = b'\272H\003\310\001\001'
   _globals['_SHUTDOWNNOTIFICATIONREQUEST'].fields_by_name['shutdownReason']._options = None
   _globals['_SHUTDOWNNOTIFICATIONREQUEST'].fields_by_name['shutdownReason']._serialized_options = b'\272H\003\310\001\001'
-  _globals['_METADATA']._serialized_start=66
-  _globals['_METADATA']._serialized_end=156
-  _globals['_KEYBULK']._serialized_start=159
-  _globals['_KEYBULK']._serialized_end=356
-  _globals['_CAPABILITIESREQUEST']._serialized_start=358
-  _globals['_CAPABILITIESREQUEST']._serialized_end=413
-  _globals['_CAPABILITIESRESPONSE']._serialized_start=415
-  _globals['_CAPABILITIESRESPONSE']._serialized_end=471
-  _globals['_PUSHKEYSREQUEST']._serialized_start=473
-  _globals['_PUSHKEYSREQUEST']._serialized_end=583
-  _globals['_PUSHKEYSRESPONSE']._serialized_start=585
-  _globals['_PUSHKEYSRESPONSE']._serialized_end=641
-  _globals['_QKDMMETADATAREQUEST']._serialized_start=643
-  _globals['_QKDMMETADATAREQUEST']._serialized_end=702
-  _globals['_QKDMMETADATARESPONSE']._serialized_start=704
-  _globals['_QKDMMETADATARESPONSE']._serialized_end=822
-  _globals['_SHUTDOWNNOTIFICATIONREQUEST']._serialized_start=824
-  _globals['_SHUTDOWNNOTIFICATIONREQUEST']._serialized_end=901
-  _globals['_SHUTDOWNNOTIFICATIONRESPONSE']._serialized_start=903
-  _globals['_SHUTDOWNNOTIFICATIONRESPONSE']._serialized_end=933
-  _globals['_KMSQKDMCOMMUNICATIONSERVICE']._serialized_start=936
-  _globals['_KMSQKDMCOMMUNICATIONSERVICE']._serialized_end=1317
+  _globals['_METADATA']._serialized_start=67
+  _globals['_METADATA']._serialized_end=269
+  _globals['_KEYBULK']._serialized_start=272
+  _globals['_KEYBULK']._serialized_end=469
+  _globals['_CAPABILITIESREQUEST']._serialized_start=471
+  _globals['_CAPABILITIESREQUEST']._serialized_end=564
+  _globals['_CAPABILITIESRESPONSE']._serialized_start=566
+  _globals['_CAPABILITIESRESPONSE']._serialized_end=660
+  _globals['_PUSHKEYSREQUEST']._serialized_start=662
+  _globals['_PUSHKEYSREQUEST']._serialized_end=772
+  _globals['_PUSHKEYSRESPONSE']._serialized_start=774
+  _globals['_PUSHKEYSRESPONSE']._serialized_end=830
+  _globals['_QKDMMETADATAREQUEST']._serialized_start=832
+  _globals['_QKDMMETADATAREQUEST']._serialized_end=891
+  _globals['_QKDMMETADATARESPONSE']._serialized_start=893
+  _globals['_QKDMMETADATARESPONSE']._serialized_end=1011
+  _globals['_SHUTDOWNNOTIFICATIONREQUEST']._serialized_start=1013
+  _globals['_SHUTDOWNNOTIFICATIONREQUEST']._serialized_end=1090
+  _globals['_SHUTDOWNNOTIFICATIONRESPONSE']._serialized_start=1092
+  _globals['_SHUTDOWNNOTIFICATIONRESPONSE']._serialized_end=1122
+  _globals['_KMSQKDMCOMMUNICATIONSERVICE']._serialized_start=1125
+  _globals['_KMSQKDMCOMMUNICATIONSERVICE']._serialized_end=1506
 # @@protoc_insertion_point(module_scope)
diff --git a/proto/quicsep/quicsep.proto b/proto/quicsep/quicsep.proto
index 6eb0f32f7cbeb22b90f7bc199d1f7e181f4989cc..43bc5928991560a02666ade046377863f4745599 100644
--- a/proto/quicsep/quicsep.proto
+++ b/proto/quicsep/quicsep.proto
@@ -25,7 +25,12 @@ message Metadata {
     int64 timestamp = 1;
     string vendor = 2;
     string version = 3;
+    uint64 keyGenerationRate = 4;
+    string sourceId = 5;
+    string destinationId = 6;
     // key generation rate?
+    // einfügen, beeinhaltet den aktuell Wert der KG, kann auch null sein
+    // source und destination ID der angrenzenden QKD-Module
     //...
 }
 
@@ -38,11 +43,14 @@ message KeyBulk {
 }
 
 message CapabilitiesRequest {
-    string version = 1 [(buf.validate.field).required = true];
+    int64 timestamp = 1 [(buf.validate.field).required = true];
+    string version = 2 [(buf.validate.field).required = true];
 }
 
 message CapabilitiesResponse {
-    string version = 1 [(buf.validate.field).required = true]; }
+    int64 timestamp = 1 [(buf.validate.field).required = true];
+    string version = 2 [(buf.validate.field).required = true];
+}
 
 message PushKeysRequest {
     int64 timestamp = 1 [(buf.validate.field).required = true];